/******************************************************************************************************
 *  The skeletion b+tree table database for Tokyo Cabinet
 *  the b+tree table can support the field index(json format value) like tct(table engine).
 *  @Author: Riceball LEE(riceballl@hotmail.com)
 *  specifiction:
 *    MUST BE an B+ TREE Table ENGINE 
 *    the key and value MUST BE utf-8 format.
 *    addint, adddouble, putcat,putproc,putdup, putdupback is never indexed
 *    outlist use the tcbdbout instead of tcbdbout3!
 *    the setindex command is same as the tct, Note the setindex do not add the original data to the index unless u re-index it.
 *      remove the index, u can use the TDBITVOID(9999) option.
 *        tcrmgr misc localhost setindex key 9999
 *      ReIndex, u can use the TDBITREINDEX(10000) option.
 *        tcrmgr misc localhost setindex key 10000
 *    the query command is same as the tct
 *
 *    Specifiction:
 *      A DB file include these file:
 *        DB Main Data File B+ Tree
 *        DB Index Data File B+ Tree ([data].idx)
 *        DB Configuration Data File Memory-Hash ([data].cfg) 
 *    Key Specifiction:
 *      the key length should be less 4095(MAXKEYLEN) chars.
 *      the duplication(same) key is not allowed.
 *      the start with "_" is NOT ALLOWED, we keep reserve as internal used.
 *      the "/", "|", "\", """ is not allowed too. u need escape these chars.
 *      the db configuration will be saved into [data].cfg
 *        _DB/index/_conf/(FieldName):
 *           (IndexOptions): the index options same as the tct's index options(add 1 when stored).[todo]
 *        _DB/index/_conf/count/(FieldName): store the index count of this (FieldName)
 *        _DB/_conf/record/count:            store the total record count in the DB
 *      the index info will be saved into [data].idx
 *        /(FieldName)/(FieldValue)/(KeyName): the index put here, store value is the field of the key. the FieldValue will be quoted("") if it is a string.
 *           value: (KeyName)
 *    Structured-Value for Index Specifiction:
 *      the "/", "|", "\", "_", """ char keep reserved. so the field value string MUST NOT USE these characters(u should escape these chars). 
 *    Structured-Value Store Specifiction:
 *      Spec.1:
 *      STRICT JSON Object only 
 *      The first char MUST BE "{" and the last char MUST BE "}" 
 *      NO MORE BLANK CHARS BE ALLOWED
 *      the string MUST BE in double quote : ""
 *      all the key in the json object must be a string.
 *      only string, number, bool, null can be indexed.
 *        Store As Json String
 *        Key: [KeyName]
 *        Value: "{"[FieldName]":[FieldValue], ...}" : the json string
 *
 *      Spec.2: (now the interface keep compatibility with tokyocabinet table engine[tct])
 *        Store As Simple Value
 *        Key: .(KeyName).(FieldName)
 *        Value: (FieldValue) : the field value
 *        Key: /(KeyName)
 *        Value: (FieldName1\0FieldName2\0FieldNameN)
 *      Spec.2.1: to speed up the performance of the server
 *        Store As a Simple Value
 *          Key: .\(keyNameCount)(KeyName)\(FieldNameCount)(FieldName)
 *          Value: (FieldValue)
 *          Key: /\(keyNameCount)(KeyName)
 *          Value: \(FieldName1Count)(FieldName1)\(FieldType1Count)(FieldType1)\FieldNameNCount(FieldNameN)\(FieldTypeNCount)(FieldTypeN)
 *        Store As a Complex Value
 *          Key: (KeyName)
 *          Value: \(FieldNameCount)(FieldName)\(FieldType)\0\(FieldValueCount)\(FieldValue)
 *        Index:
 *          Key: /\(FieldNameCount)(FieldName)\(FieldValueCount)(FieldValue)\(KeyNameCount)(KeyName)
 *          Value: (KeyName)
 *        Config:
 *          + Key: _DB/data/ver: the db data structure version
 *            Value: 2.1
 *
 #test :
 tcrmgr vanish localhost 
 tcrmgr misc localhost setindex key 0 
 tcrmgr misc localhost setindex goodkey 0 
 tcrmgr misc localhost setindex key1 0 
 tcrmgr misc localhost setindex key2 0
 tcrmgr list localhost 
 #we can update to all or update a single field now:
 tcrmgr put -sep "|" localhost mykey "key|hello|goodkey|false" 
 tcrmgr put -sep "|" localhost "mykey.key" "hello" 
 #tcrmgr put localhost mykey "{\"key\":\"hello\",\"goodkey\":false}" #deprecated
 #tcrmgr put localhost mykey2 "{\"key\":\"你好\",\"goodkey\":true}" #deprecated
 tcrmgr list localhost 
 mykey
 mykey2

 tchmgr list data.cfg
 _DB/index/_conf/goodkey
 _DB/index/_conf/key
 _DB/index/_conf/key1
 _DB/index/_conf/key2
 
 tcbmgr list data.idx
 _DB/index/goodkey/false/mykey
 _DB/index/goodkey/true/mykey2
 _DB/index/key/"hello"
 _DB/index/key/"你好"
 
 #tcrmgr misc localhost put mykey3 "{\"key\":\"hi,every body\",\"goodkey\":null, \"opt\":0, \"key2\":"same thing"}" #deprecated

 
 //remove the key index
 tcrmgr misc localhost setindex key 9999

 *
 *    
 *                                          Copyright (C) 2009-2010 CloudDatabase Team
 ******************************************************************************************************/


#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <uuid/uuid.h>
//#include <jansson.h>  //json 
//#include "tcjson.h"

#include "tcutil.h"
#include "tchdb.h"
#include "tcbdb.h"
#include "tcadb.h"
#include "tctdb.h"
#include "tcskelbdb.h"
#include "myconf.h"

//for get available physical memory:
#if defined(_SYS_FREEBSD_) || defined(_SYS_MACOSX_)
  #include <sys/sysctl.h> //hardware vir-memory
#elif defined(_SYS_LINUX_)
  #include <sys/sysinfo.h>
#elif defined(_SYS_SUNOS_)
  #include <unistd.h>
#elif defined(_SYS_HPUX_)
  #include <sys/pstat.h> 
#endif

#define TCNUMBUFSIZ    32                // size of a buffer for a number
#if defined(DEBUG)
  #define MAXLISTSIZE    2               //-1 means get all items at once.
#else
  #define MAXLISTSIZE    4096*16         //-1 means get all items at once.
#endif
//#define DEBUG

#define URN_TEMPLATE "urn:uid:"

#define DBLOCKMETHOD(TC_db, TC_wr)                            \
(((TBTreeDatabase *) TC_db)->mmtx ? tcDBLockMethod((TC_db), (TC_wr)) : true)
#define DBUNLOCKMETHOD(TC_db)                         \
(((TBTreeDatabase *) TC_db)->mmtx ? tcDBUnLockMethod(TC_db) : true)
#define DBTHREADYIELD(TC_db)                          \
do { if(((TBTreeDatabase *) TC_db)->mmtx) sched_yield(); } while(false)

#if defined __GNUC__
#define DisableWarning #pragma GCC system_header
#define EnableWarning  
#elif defined __SUNPRO_CC
#define DisableWarning #pragma disable_warn
#define EnableWarning  #pragma enable_warn
#elif defined _MSC_VER
#define DisableWarning #pragma warning(push, 1)
#define EnableWarning #pragma warning(pop) 
#endif

#define TDBDEFBNUM     131071            // default bucket number
#define TDBDEFAPOW     4                 // default alignment power
#define TDBDEFFPOW     10                // default free block pool power
#define TDBDEFLCNUM    4096              // default number of leaf cache
#define TDBDEFNCNUM    512               // default number of node cache
#define TDBDEFXMSIZ    (64LL<<20)        // default size of the extra mapped memory

#define TDBNUMCOLMAX   16                // maximum number of columns of the long double
#define TDBHINTUSIZ    256               // unit size of the hint string
#define TDBORDRATIO    0.2               // ratio of records to use the order index

typedef struct {                         // type of structure for a sort record
  const char *kbuf;                      // pointer to the primary key
  int ksiz;                              // size of the primary key
  char *vbuf;                            // pointer to the value
  int vsiz;                              // size of the value
} TDBSORTREC;

enum {                                   // enumeration for duplication behavior
  TDBPDOVER,                             // overwrite an existing value
  TDBPDKEEP,                             // keep the existing value
  TDBPDCAT,                               // concatenate values
  TDBPDDUP,                              // allow duplication of keys
  TDBPDDUPB,                             // allow backward duplication
  TDBPDADDINT,                           // add an integer
  TDBPDADDDBL,                           // add a real number
  TDBPDPROC                              // process by a callback function
};

/* private function protOrderTypes */
static bool tcDBLockMethod(TBTreeDatabase *db, bool wr);
static bool tcDBUnLockMethod(TBTreeDatabase *db);
static bool tcDBOpenImpl(TBTreeDatabase *aDB, const char *aName);
static bool tcDBCloseImpl(TBTreeDatabase *aDB);
static bool tcDBTransAbortImpl(TBTreeDatabase *aDB);
static void *tcDBGetImpl(TBTreeDatabase *aDB, const char *aKey, int aKeySize, int *aValueSize);
static bool tcDBDeleteImpl(TBTreeDatabase *aDB, const char *kbuf, int ksiz, const TCBDBOUTPROC aOutProc);
static bool tcDBReplaceImpl(TBTreeDatabase *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int aDMode);
//internal put the field's index.
static inline bool tcDBPutFieldIndexImpl(TBTreeDatabase *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz);
//internal delete the key's index.
static inline bool tcDBDeleteIndexImpl(TBTreeDatabase *aDB, const char *kbuf, int ksiz);
static bool tcDBDeleteFieldIndexImpl(TBTreeDatabase *aDB, const char *kbuf, int ksiz);
static uint64_t tcDBGetMatchedKeysCount(TBTreeDatabase *aDB, const char *aKey, int aKeySize);
static bool tcDBDeleteMatchedKeysImpl(TBTreeDatabase *aDB, const char *aKey, int aKeySize);
static bool tcDBPutColumnsImpl(TBTreeDatabase *aDB, const char *aKey, int aKeySize, TCMAP *aColumns, int aDMode);
bool tcDBMemSync(void *aDB, bool phys);

//the old name is tcDBSetEcode
/* Set the error code of a database object. */
/* raise some critical error will halt progrm, some minro error will report error only.*/
void tcDBRaiseError(void *aDB, int aErrCode, const char *aFilename, int aLine, const char *aFunc);


//------------------the interface of a DB
void* tcDBNew(void);
void  tcDBFree(void *aDB);
bool tcDBOpen(void *aDB, const char *aName);
bool tcDBClose(void *aDB);
//add/update a record(key/calue)
bool tcDBReplace(void *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz);
//add a record. return false if key exists or other error.
bool tcDBInsert(void *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz);
bool tcDBDelete(void *aDB, const char *kbuf, int ksiz);
void *tcDBGet(void *aDB, const char *aKey, int aKeySize, int *aValueSize);
/* Get the size of the value of a record.
 If successful, the return value is the size of the value of the corresponding record, else,
 it is -1. */
int tcDBGetValueSize(void *aDB, const char *aKey, int aKeySize);
/* Initialize the iterator of a reocrd.
 If successful, the return value is true, else, it is false.
 The iterator is used in order to access the key of every record stored in a database. */
bool tcDBIterInit(void *aDB);
/* Get the next key of the iterator of a database object.
 `aDB' specifies the database object.
 `aSize' specifies the pointer to the variable into which the size of the region of the return
 value is assigned.
 If successful, the return value is the pointer to the region of the next key, else, it is
 `NULL'.  `NULL' is returned when no record is to be get out of the iterator.
 Because an additional zero code is appended at the end of the region of the return value, the
 return value can be treated as a character string.  Because the region of the return value is
 allocated with the `malloc' call, it should be released with the `free' call when it is no
 longer in use. */
void *tcDBIterNext(void *aDB, int *aSize);
/* Get forward matched keys in a database object: the first part string is matched with the aKey.
 `aDB' specifies the database object.
 `aKey' specifies the pointer to the region of the prefix.
 `aKeySize' specifies the size of the region of the prefix.
 `aMaxCount' specifies the maximum number of keys to be fetched.  If it is negative, no limit is
 specified.
 The return value is a list object of the corresponding keys.  This function does never fail.
 It returns an empty list even if no key corresponds.
 Because the object of the return value is created with the function `tclistnew', it should be
 deleted with the function `tclistdel' when it is no longer in use.  Note that this function
 may be very slow because every key in the database is scanned. */
TCLIST *tcDBGetMatchedKeys(void *aDB, const char *aKey, int aKeySize, int aMaxCount);
/* Add an integer to a record in a database object.
 `aDB' specifies the abstract database object.
 `aKey' specifies the pointer to the region of the key.
 `aKeySize' specifies the size of the region of the key.
 `aNumber' specifies the additional value.
 If successful, the return value is the summation value, else, it is `INT_MIN'.
 If the corresponding record exists, the value is treated as an integer and is added to.  If no
 record corresponds, a new record of the additional value is stored. */
int tcDBAddInt(void *aDB, const char *aKey, int aKeySize, int aNumber);
double tcDBAddDouble(void *aDB, const char *aKey, int aKeySize, double aNumber);
//append the value to an exists record.
bool tcDBAddString(void *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz);
bool tcDBSync(void *aDB);
bool tcDBOptimize(void *aDB, const char *aParams);
bool tcDBClear(void *aDB);
bool tcDBCopy(void *aDB, const char *aPath);
bool tcDBTransBegin(void *aDB);
bool tcDBTransCommit(void *aDB);
bool tcDBTransAbort(void *aDB);
const char *tcDBPath(void *aDB);
//get the record number of the db //exclude the index number and index config.
uint64_t tcDBGetRecordNum(void *aDB);
uint64_t tcDBSize(void *aDB);
//execute the additional commands in misc.
TCLIST *tcDBExecCmd(void *aDB, const char *name, const TCLIST *args);
bool tcDBReplaceProcDup(void *aDB, const void *aKey, int aKeySize, const void *aValue, int aValueSize,
                            TCPDPROC aProc, void *aProcParams);
bool tcDBForEach(void *aDB, TCITER aIterProc, void *aIterParams);
//------------------the interface of a DB---END-----
bool tcDBIndexRemove(void *aDB, const char *aFieldbuf, int aFieldSiz);
/* set an index to a field */
bool tcDBIndexSet(TBTreeDatabase *aDB, const char *aFieldbuf, int aFieldSiz, const char *aOptions, int aOptionsSiz, int aOptionInt);
/* remove the Key with index supports */
//bool tcDBDeleteProc(TBTreeDatabase *aDB, const char *kbuf, int ksiz, const TCBDBOUTPROC aOutProc);
/* add/update Key/Structured-Value with index supports */
//bool tcDBReplaceProc(TBTreeDatabase *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz, const TCBDBPUTPROC aPutProc);
bool tcDBDefrag(TBTreeDatabase *aDB, int64_t aStep);
uint64_t tcDBGetIndexNum(TBTreeDatabase *aDB);
uint64_t tcDBGetConfigNum(TBTreeDatabase *aDB);
int tcDBIndexGetValueSize(void *aDB, const char *aKey, int aKeySize);
int tcDBConfigGetValueSize(void *aDB, const char *aKey, int aKeySize);
TCLIST *tcDBIndexGetMatchedKeys(void *aDB, const char *aKey, int aKeySize, int aMaxCount);
TDBQuery *tcDBQueryCreate(void *aDB);
void tcDBQueryFree(TDBQuery *qry);
bool tcDBQueryProc(TDBQuery *qry, TDBQRYPROC proc, void *op);
int tcDBQueryCount(TDBQuery *qry);
TCLIST *tcDBQuerySearch(TDBQuery *qry);
int64_t tcDBGenUID(void *aDB);

/* add/update Key/Structured-Value with index supports */
//static bool tcADBReplace(TCADB *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz, const TCADBPUTPROC aPutProc);
/* remove the Key with index supports */
//static bool tcADBDelete(TCADB *aDB, const char *kbuf, int ksiz);
/* remove/clear an index of a field  撤销(删除)字段上的索引 */
static bool tcBDBIndexRemove(TCBDB *aDB, const char *aFieldbuf, int aFieldSiz);
//static bool tcBDBIndexClear(TCBDB *aDB, const char *kbuf, int ksiz);

/*************************************************************************************************
 * API
 *************************************************************************************************/
inline size_t getAvailablePhysMemory() {
#if defined(_SYS_FREEBSD_) || defined(_SYS_MACOSX_)
  int mib[2];
  size_t len;
  size_t vResult;
  mib[0] = CTL_HW;
  mib[1] = HW_PHYSMEM;
  len =sizeof(vResult);
  sysctl(mib, 2, &vResult, &len, NULL, 0);
  return vResult;
#elif defined(_SYS_LINUX_)
  struct sysinfo si;
  if ( sysinfo(&si) == 0) {
    return si.freeram; //the freehigh only available on linux 2.3.23 above.
  }
  else {
    return -1;
  }

#elif defined(_SYS_SUNOS_)
  return sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGE_SIZE);
#elif defined(_SYS_HPUX_)
  struct pst_static pst;
  if (pstat_getstatic(&pst, sizeof(pst), (size_t) 1, 0) != -1)
  {
    return pst.physical_memory * pst.page_size;
  }
  else {
    return -1;
  }

#else
  return -1;
#endif
}

/* u should free the result by youself. */
static inline char *GenerateUID(void) {
  uuid_t uu;
  char *char_uu; //[sizeof(URN_TEMPLATE) + 37];
  char_uu = tcmalloc(37);
  
  //strcpy (char_uu, URN_TEMPLATE);
  
  uuid_generate (uu);
  //uuid_unparse (uu, char_uu + sizeof (URN_TEMPLATE) - 1);
  uuid_unparse (uu, char_uu);
#if defined(DEBUG)
  fprintf (stderr, "uid:%s\n", char_uu);
#endif
  uuid_clear (uu);
  
  return (char_uu);  
}

static inline char *GetFieldValueFromIndexKey(const char* aIndexKey, const int aFieldNameSize) {
  char *vResult = aIndexKey;
  vResult += strlen(KEYSEPSTR);
  if (aFieldNameSize != 0) {
    vResult += strlen(IDXKEY) + aFieldNameSize;// + strlen(KEYSEPSTR);
  }
  //char * pch = strrchr(aIndexKey, '\0');
  return vResult;
}

static inline bool isKeepReservedChar(const char *aStr) {
  return (*aStr == '_' || *aStr == '/' || *aStr == '|' || *aStr == '\\' || *aStr == '"');
}

/* Generate a unique ID number.
 `tdb' specifies the table database object.
 `inc' specifies the increment of the seed.
 The return value is the new unique ID number or -1 on failure. */
static int64_t tcDBGenUIDImpl(TCBDB *aDB, int64_t inc){
  assert(aDB);
  void *opq = tchdbopaque(aDB->hdb);
  uint64_t llnum, uid;
  if(inc < 0){
    uid = -inc - 1;
  } else {
    memcpy(&llnum, opq, sizeof(llnum));
    if(inc == 0) return TCITOHLL(llnum);
    uid = TCITOHLL(llnum) + inc;
  }
  llnum = TCITOHLL(uid);
  memcpy(opq, &llnum, sizeof(llnum));
  return uid;
}

//str cat json value(string, number, bool, null)
//static inline void StrCatJson(char* aStr, const void* aJsonValue) {
//  char numbuf[TCNUMBUFSIZ];
//  switch (json_typeof((json_t*)aJsonValue)) {
//    case JSON_STRING:
//      strcat(aStr, "\"");
//      strcat(aStr, json_string_value((json_t*)aJsonValue));
//      strcat(aStr, "\"");
//      break;
//    case JSON_INTEGER:
//      sprintf(numbuf, "%d", json_integer_value((json_t*)aJsonValue));
//      strcat(aStr, numbuf);
//      break;
//    case JSON_REAL:
//      sprintf(numbuf, "%f", json_real_value((json_t*)aJsonValue));
//      strcat(aStr, numbuf);
//    case JSON_TRUE:
//      strcat(aStr, "true");
//      break;
//    case JSON_FALSE:
//      strcat(aStr, "false");
//      break;
//    case JSON_NULL:
//      strcat(aStr, "null");
//      break;
//    default:
//      break;
//  }
//  
//}

static void tcDBSkelSet(ADBSKEL *skel){
#if defined(DEBUG)
  //fprintf(stderr, "%s\n", "tcDBSkelSet");
#endif
  skel->opq = tcDBNew();
  skel->del = (void (*)(void *))tcDBFree;
  skel->open = (bool (*)(void *, const char *))tcDBOpen;
  skel->close = (bool (*)(void *))tcDBClose;
  skel->put = (bool (*)(void *, const void *, int, const void *, int))tcDBReplace;
  skel->putkeep = (bool (*)(void *, const void *, int, const void *, int))tcDBInsert;
  skel->putcat = (bool (*)(void *, const void *, int, const void *, int))tcDBAddString;
  skel->out = (bool (*)(void *, const void *, int))tcDBDelete;
  skel->get = (void *(*)(void *, const void *, int, int *))tcDBGet;
  skel->vsiz = (int (*)(void *, const void *, int))tcDBGetValueSize;
  skel->iterinit = (bool (*)(void *))tcDBIterInit;
  skel->iternext = (void *(*)(void *, int *))tcDBIterNext;
  skel->fwmkeys = (TCLIST *(*)(void *, const void *, int, int))tcDBGetMatchedKeys;
  skel->addint = (int (*)(void *, const void *, int, int))tcDBAddInt;
  skel->adddouble = (double (*)(void *, const void *, int, double))tcDBAddDouble;
  skel->sync = (bool (*)(void *))tcDBSync;
  skel->optimize = (bool (*)(void *, const char *))tcDBOptimize;
  skel->vanish = (bool (*)(void *))tcDBClear;
  skel->copy = (bool (*)(void *, const char *))tcDBCopy;
  skel->tranbegin = (bool (*)(void *))tcDBTransBegin;
  skel->trancommit = (bool (*)(void *))tcDBTransCommit;
  skel->tranabort = (bool (*)(void *))tcDBTransAbort;
  skel->path = (const char *(*)(void *))tcDBPath;
  skel->rnum = (uint64_t (*)(void *))tcDBGetRecordNum;
  skel->size = (uint64_t (*)(void *))tcDBSize;
  skel->misc = (TCLIST *(*)(void *, const char *, const TCLIST *))tcDBExecCmd;
  skel->putproc =
  (bool (*)(void *, const void *, int, const void *, int, TCPDPROC, void *))tcDBReplaceProcDup;
  skel->foreach = (bool (*)(void *, TCITER, void *))tcDBForEach;
  
}
/* Set the b+tree database skeleton to an abstract database object. */
bool tcADBSetSkelDB(TCADB *adb){
#if defined(DEBUG)
  //fprintf(stderr, "%s\n", "tcADBSetSkelDB");
#endif
  assert(adb);
  if(adb->omode != ADBOVOID) return false;
  ADBSKEL skel;
  memset(&skel, 0, sizeof(skel));
  tcDBSkelSet(&skel);
  if(!tcadbsetskel(adb, &skel)){
    tcDBFree(skel.opq);
    return false;
  }
  return true;
}

bool initialize(ADBSKEL *skel){
  tcDBSkelSet(skel);
  return true;
}

/* Add an int64 to a record in a hash database object.
 `aDB' specifies the hash database object connected as a writer.
 `kbuf' specifies the pointer to the region of the key.
 `ksiz' specifies the size of the region of the key.
 `num' specifies the additional value.
 If successful, the return value is the summation value, else, it is `0'.
 If the corresponding record exists, the value is treated as an integer and is added to.  If no
 record corresponds, a new record of the additional value is stored. */
static uint64_t tcHDBAddUInt64(TCHDB *aDB, const char *aKey, const int aKeySize, const int64_t aValue) {
  assert(aDB && aKey && aKeySize >= 0);
  uint64_t vResult;
  if (tchdbget3(aDB, aKey, aKeySize, &vResult, sizeof(vResult)) == -1) {
    vResult = 0;
  };
  vResult += aValue;
  if (!tchdbput(aDB, aKey, aKeySize, &vResult, sizeof(vResult))) {
    tchdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, "(hdb)add uint64 failed");
    vResult = 0;
  }
  return vResult;
  
}

/* Generate a unique ID number of a table database object. */
int64_t tcDBGenUID(void *aDB){
  assert(aDB);
  if(!DBLOCKMETHOD(aDB, true)) return -1;
  if(!((TBTreeDatabase*)aDB)->Opened || !((TBTreeDatabase*)aDB)->Writable){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return -1;
  }
  int64_t rv = tcDBGenUIDImpl(((TBTreeDatabase*)aDB)->DB, 1);
  DBUNLOCKMETHOD(aDB);
  return rv;
}

/* Set mutual exclusion control of a database object for threading. */
bool tcDBSetMutex(TBTreeDatabase *aDB){
  assert(aDB);
  if(!TCUSEPTHREAD) return true;
  if(aDB->mmtx || aDB->Opened){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    return false;
  }
  TCMALLOC(aDB->mmtx, sizeof(pthread_rwlock_t));
  bool vResult = (pthread_rwlock_init(aDB->mmtx, NULL) == 0);
  if(!vResult){
    TCFREE(aDB->mmtx);
    aDB->mmtx = NULL;
    //return vResult;
  }
//  vResult = tcbdbsetmutex(aDB->DB);
//  if (vResult) {
//    vResult = tcbdbsetmutex(aDB->IndexDB);
//    if (vResult) vResult = tchdbsetmutex(aDB->ConfigDB);
//  }
  return vResult;
}

//*/

//const char *tcSkelBDBErrMsg(int ecode){
//  switch (ecode) {
//    case TCEREMOVEINDEX: return "remove an index failed.";
//    default: return tcbdberrmsg(ecode);
//  }
//}

static void tcDBStructClear(TBTreeDatabase *vDB){
  assert(vDB);
  vDB->IndexDB = NULL;
  vDB->ConfigDB = NULL;
  vDB->DB = NULL;
  vDB->Cursor = NULL;
  vDB->mmtx = NULL;
  vDB->Opened = false;
  vDB->Writable = false;
  vDB->InTransaction = false;
}

/*************************************************************************************************
 * Interface features
 *************************************************************************************************/
void* tcDBNew(void){
  register TBTreeDatabase *vDB;
  TCMALLOC(vDB, sizeof(*vDB));
  vDB->DB = tcbdbnew();
  vDB->IndexDB = tcbdbnew();
  vDB->ConfigDB = tchdbnew();
  vDB->Cursor = NULL;
  //vDB->Cursor = tcbdbcurnew(vDB->DB);
  //vDB->mmtx = NULL;
  vDB->Opened = false;
  vDB->Writable = false;
  vDB->InTransaction = false;
  tcDBSetMutex(vDB);
  return vDB;
}

#define BDBFREE(TC_db)               \
  tcbdbdel(TC_db);                   \
  TC_db = NULL;
#define HDBFREE(TC_db)               \
  tchdbdel(TC_db);                   \
  TC_db = NULL;

void tcDBFree(void *aDB){
  assert(aDB);
  register TBTreeDatabase* vDB = (TBTreeDatabase*) aDB;
  if(vDB->Opened) tcDBClose(vDB);
  BDBFREE(vDB->DB);
  BDBFREE(vDB->IndexDB);
  HDBFREE(vDB->ConfigDB);
  
  if(vDB->mmtx){
    pthread_rwlock_destroy(vDB->mmtx);
    TCFREE(vDB->mmtx);
  }
  TCFREE(vDB);
}

#define BDBCLOSE(TC_db, TC_Result)               \
  if(!tcbdbclose(TC_db)) TC_Result = false;      
#define HDBCLOSE(TC_db, TC_Result)               \
  if(!tchdbclose(TC_db)) TC_Result = false;      

static bool tcDBCloseImpl(TBTreeDatabase *aDB) {
  bool vResult = !(aDB->InTransaction) || tcDBTransAbortImpl(aDB);
  tcbdbcurdel(aDB->Cursor);
  BDBCLOSE(aDB->DB, vResult);
  BDBCLOSE(aDB->IndexDB, vResult);
  HDBCLOSE(aDB->ConfigDB, vResult);
  aDB->Opened = false;
  aDB->Writable = false;
  return vResult;  
}

bool tcDBClose(void *aDB) {
  assert(aDB);
  TBTreeDatabase* vDB = (TBTreeDatabase*) aDB;
  if(!DBLOCKMETHOD(aDB, true)) return false;
  if(!(vDB->Opened)){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  bool vResult = tcDBCloseImpl(vDB);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

bool tcDBAddString(void *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz){
  assert(aDB && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
  return tcbdbputcat(((TBTreeDatabase*)aDB)->DB, kbuf, ksiz, vbuf, vsiz);
}

static bool tcDBDeleteMatchedKeysImpl(TBTreeDatabase *aDB, const char *aKey, int aKeySize) {
  BDBCUR *vCur = tcbdbcurnew(aDB->DB);
  tcbdbcurjump(vCur, aKey, aKeySize);
  bool vResult = true;
  while(vCur->id > 0){
    int ksiz;
    const char *kbuf = tcbdbcurkey3(vCur, &ksiz);
    
    if(!kbuf){
      //if(tchdbecode(bdb->hdb) != TCEINVALID && tchdbecode(bdb->hdb) != TCENOREC) err = true;
      vResult = false;
      break;
    }
    if(ksiz < aKeySize || memcmp(kbuf, aKey, aKeySize)) break;
    tcDBDeleteFieldIndexImpl(aDB, kbuf, ksiz);
    vResult = tcbdbcurout(vCur);
    //if (!tcbdbcurout(vCur)) vResult = false;
  }
  tcbdbcurdel(vCur);
  //printf("result=%d\n", vResult);
  return vResult;
}

static uint64_t tcDBGetMatchedKeysCount(TBTreeDatabase *aDB, const char *aKey, int aKeySize) {
  uint64_t vResult = 0;
  BDBCUR *vCur = tcbdbcurnew(aDB->DB);
  tcbdbcurjump(vCur, aKey, aKeySize);
  const char *lbuf = NULL;
  int lsiz = 0;
  while(vCur->id > 0){
    int ksiz;
    const char *kbuf = tcbdbcurkey3(vCur, &ksiz);
    
    if(!kbuf){
      //if(tchdbecode(bdb->hdb) != TCEINVALID && tchdbecode(bdb->hdb) != TCENOREC) err = true;
      break;
    }
    if(ksiz < aKeySize || memcmp(kbuf, aKey, aKeySize)) break;
    if(!lbuf || lsiz != ksiz || memcmp(kbuf, lbuf, ksiz)){ //ignore the duplication keys
      vResult = vResult + 1;
      //TCLISTPUSH(keys, kbuf, ksiz);
      //if(TCLISTNUM(keys) >= max) break;
      lbuf = kbuf;
      lsiz = ksiz;
    }
    tcbdbcurnext(vCur);
  }
  tcbdbcurdel(vCur);
  return vResult;
}


//column name and value seperated by "\0", like this: columnName\0ColumnValue\ColumnName\0ColumnValue
static inline void *tcDBGetColumnValuesStr(TBTreeDatabase *aDB, const char *aKey, int aKeySize, int *aValueSize) {
  void *vResult = NULL; 
  char vMainKeyBuf[MAXKEYLEN+1];
  strcpy(vMainKeyBuf, KEYSEPSTR);
  strncat(vMainKeyBuf, aKey, aKeySize);
  int vSColumnsSize;
  char *vSColumns = tcbdbget(aDB->DB, vMainKeyBuf, aKeySize+strlen(KEYSEPSTR), &vSColumnsSize);
  if (vSColumns) {
    strcpy(vMainKeyBuf, FIELDSEPSTR);
    strncat(vMainKeyBuf, aKey, aKeySize);
    strcat(vMainKeyBuf, FIELDSEPSTR);
    int vMainKeySize = aKeySize+strlen(FIELDSEPSTR)+strlen(FIELDSEPSTR);
    TCLIST *vColumns = tcstrsplit2(vSColumns, vSColumnsSize);
    TCFREE(vSColumns);
    TCXSTR *vS = tcxstrnew3(512);
    for (int i=0; i < tclistnum(vColumns); i++) {
      const char *vFieldKey;
      int vFieldKeySize;
      TCLISTVAL(vFieldKey, vColumns, i, vFieldKeySize);
      strncat(vMainKeyBuf, vFieldKey, vFieldKeySize);
      int vFieldValueSize;
      char *vFieldValue = tcbdbget(aDB->DB, vMainKeyBuf, vMainKeySize+vFieldKeySize, &vFieldValueSize);
      if (vFieldValue) {
        TCXSTRCAT(vS, vFieldKey, vFieldKeySize+1);
        TCXSTRCAT(vS, vFieldValue, vFieldValueSize+1);
        MYFREE(vFieldValue);
      }
      vMainKeyBuf[vMainKeySize] = '\0';
    }
    tclistdel(vColumns);
    if (TCXSTRSIZE(vS) > 0) {
      vResult = tcxstrtomalloc(vS);
      *aValueSize = TCXSTRSIZE(vS)?TCXSTRSIZE(vS)-1:0;
    }
    else {
      tcxstrdel(vS);
      *aValueSize = 0;
    }
  } //vSColumns
  else {
    vResult = tcbdbget(aDB->DB, aKey, aKeySize, aValueSize);
  }
  return vResult;
}

static inline TCMAP *tcDBGetColumnValuesMapFromStr(TBTreeDatabase *aDB, const char *aKey, int aKeySize, const char *aSColumns, const int aSColumnsSize) {
  TCMAP *vResult = NULL;//tcmapnew();
  char vMainKeyBuf[MAXKEYLEN+1];
  
  strcpy(vMainKeyBuf, FIELDSEPSTR);
  strncat(vMainKeyBuf, aKey, aKeySize);
  strcat(vMainKeyBuf, FIELDSEPSTR);
  int vMainKeySize = aKeySize+strlen(FIELDSEPSTR)+strlen(FIELDSEPSTR);
  TCLIST *vColumns = tcstrsplit2(aSColumns, aSColumnsSize);
  vResult = tcmapnew();
  for (int i=0; i < tclistnum(vColumns); i++) {
    const char *vFieldKey;
    int vFieldKeySize;
    TCLISTVAL(vFieldKey, vColumns, i, vFieldKeySize);
    strncat(vMainKeyBuf, vFieldKey, vFieldKeySize);
    int vFieldValueSize;
    char *vFieldValue = tcbdbget(aDB->DB, vMainKeyBuf, vMainKeySize+vFieldKeySize, &vFieldValueSize);
    if (vFieldValue) {
      tcmapput(vResult,vFieldKey, vFieldKeySize, vFieldValue, vFieldValueSize);
      TCFREE(vFieldValue);
    }
    vMainKeyBuf[vMainKeySize] = '\0';
  }
  tclistdel(vColumns);
  if (tcmaprnum(vResult) == 0) {
    tcmapdel(vResult);
    vResult = NULL;
  }
  return vResult;
}

static inline TCMAP *tcDBGetColumnValuesMapImpl(TBTreeDatabase *aDB, const char *aKey, int aKeySize) {
  TCMAP *vResult = NULL;//tcmapnew();
  int vSColumnsSize;
  char vMainKeyBuf[MAXKEYLEN+1];
  strcpy(vMainKeyBuf, KEYSEPSTR);
  strncat(vMainKeyBuf, aKey, aKeySize);
  char *vSColumns = tcbdbget(aDB->DB, vMainKeyBuf, aKeySize+strlen(KEYSEPSTR), &vSColumnsSize);
  if (vSColumns) {
    strcpy(vMainKeyBuf, FIELDSEPSTR);
    strncat(vMainKeyBuf, aKey, aKeySize);
    strcat(vMainKeyBuf, FIELDSEPSTR);
    int vMainKeySize = aKeySize+strlen(FIELDSEPSTR)+strlen(FIELDSEPSTR);
    TCLIST *vColumns = tcstrsplit2(vSColumns, vSColumnsSize);
    TCFREE(vSColumns);
    vResult = tcmapnew();
    for (int i=0; i < tclistnum(vColumns); i++) {
      const char *vFieldKey;
      int vFieldKeySize;
      TCLISTVAL(vFieldKey, vColumns, i, vFieldKeySize);
      strncat(vMainKeyBuf, vFieldKey, vFieldKeySize);
      int vFieldValueSize;
      char *vFieldValue = tcbdbget(aDB->DB, vMainKeyBuf, vMainKeySize+vFieldKeySize, &vFieldValueSize);
      if (vFieldValue) {
        tcmapput(vResult,vFieldKey, vFieldKeySize, vFieldValue, vFieldValueSize);
        TCFREE(vFieldValue);
      }
      vMainKeyBuf[vMainKeySize] = '\0';
    }
    tclistdel(vColumns);
    if (tcmaprnum(vResult) == 0) {
      tcmapdel(vResult);
      vResult = NULL;
    }
  }
  else {
    char *vS = tcbdbget(aDB->DB, aKey, aKeySize, &vSColumnsSize);
    if (vS) {
      vResult = tcmapnew();
      tcmapput(vResult, aKey, aKeySize, vS, vSColumnsSize);
      TCFREE(vS);
    }
  }
  return vResult;
}

TCMAP *tcDBGetColumnValuesMap(void *aDB, const char *aKey, int aKeySize) {
  assert(aDB && aKey && aKeySize >= 0);
  if(!DBLOCKMETHOD(aDB, false)) return NULL;
  if(((TBTreeDatabase*)aDB)->DB == NULL){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return NULL;
  }
  TCMAP *vResult = tcDBGetColumnValuesMapImpl(((TBTreeDatabase*)aDB), aKey, aKeySize);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

static inline void *tcDBGetFieldValueStr(TBTreeDatabase *aDB, const char *aKey, int aKeySize, const char* aFieldName, int aFieldNameSize, int *aValueSize) {
  void* vResult = NULL;
  char stack[MAXKEYLEN], *rbuf, *vP;
  int vsiz = strlen(FIELDSEPSTR) + aKeySize + strlen(FIELDSEPSTR) + aFieldNameSize;
  if (vsiz  < sizeof(stack)) {
    rbuf = stack;
  } else {
    TCMALLOC(rbuf, vsiz+1);
  }
  vP = rbuf;
  memcpy(vP, FIELDSEPSTR, strlen(FIELDSEPSTR));
  vP += strlen(FIELDSEPSTR);
  memcpy(vP, aKey, aKeySize);
  vP += aKeySize;
  memcpy(vP, FIELDSEPSTR, strlen(FIELDSEPSTR));
  vP += strlen(FIELDSEPSTR);
  memcpy(vP, aFieldName, aFieldNameSize);
  //vP += aFieldNameSize;
  vResult = tcbdbget(aDB->DB, rbuf, vsiz, aValueSize);
  if(rbuf != stack) TCFREE(rbuf);
  return vResult;
}

static void *tcDBGetImpl(TBTreeDatabase *aDB, const char *aKey, int aKeySize, int *aValueSize){
  void *vResult = NULL; 
  if ((aKey[0] == FIELDSEPCHAR) || isKeepReservedChar(aKey)) { //a single field storage.
    vResult = tcbdbget(aDB->DB, aKey, aKeySize, aValueSize);
  }
  else {
    vResult = tcDBGetColumnValuesStr(aDB, aKey, aKeySize, aValueSize);
/*    
    strcpy(vMainKeyBuf, FIELDSEPSTR);
    strncat(vMainKeyBuf, aKey, aKeySize);
    strcat(vMainKeyBuf, FIELDSEPSTR);
    //printf("fw:%s\n", vMainKeyBuf);
    int vMainKeySize = aKeySize+strlen(FIELDSEPSTR)+strlen(FIELDSEPSTR);
    TCLIST *vKeys = tcbdbfwmkeys(aDB->DB, vMainKeyBuf, vMainKeySize, -1);
    if(!vKeys || (tclistnum(vKeys) <= 0)){
      tcDBRaiseError(aDB, TCENOREC, __FILE__, __LINE__, __func__);
      tclistdel(vKeys);
      return NULL;
    }
    TCXSTR *vTemp = tcxstrnew();
    for(int i = 0; i < tclistnum(vKeys); i++){
      int vKsiz;
      const char *vKey = tclistval(vKeys, i, &vKsiz);
      int vsiz;
      char *vbuf = tcbdbget(aDB->DB, vKey, vKsiz, &vsiz);
      //printf("(%d). '%s' = '%s'\n", i, vKey, vbuf);
      TCXSTRCAT(vTemp, vKey+vMainKeySize, vKsiz-vMainKeySize+1);
      //TCXSTRCAT(vTemp, "\0", 1);
      TCXSTRCAT(vTemp, vbuf, vsiz+1);
      //TCXSTRCAT(vTemp, "\0", 1);
      free(vbuf);
    }
    tclistdel(vKeys);
    //TCXSTRCAT(vTemp, "\0", 1);
    *aValueSize = tcxstrsize(vTemp)?tcxstrsize(vTemp)-1:0;
    //if (*aValueSize > 0) *aValueSize = *aValueSize - 1;
    //printf("siz:%d\n", *aValueSize);
    vResult = tcxstrtomalloc(vTemp);
    //exit(0);
 */
  }
  return vResult;
}

void *tcDBGetFieldValue(void *aDB, const char *aKey, int aKeySize, const char* aFieldName, int aFieldNameSize, int *aValueSize) {
  assert(aDB && aKey && aKeySize >= 0 && aFieldName && aFieldNameSize >= 0 && aValueSize);
  if(!DBLOCKMETHOD(aDB, false)) return NULL;
  if(((TBTreeDatabase*)aDB)->DB == NULL){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return NULL;
  }
  void *vResult = tcDBGetFieldValueStr(((TBTreeDatabase*)aDB), aKey, aKeySize, aFieldName, aFieldNameSize, aValueSize);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

void *tcDBGet(void *aDB, const char *aKey, int aKeySize, int *aValueSize){
  assert(aDB && aKey && aKeySize >= 0 && aValueSize);
  if(!DBLOCKMETHOD(aDB, false)) return NULL;
  if(((TBTreeDatabase*)aDB)->DB == NULL){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return NULL;
  }
  void *vResult = tcDBGetImpl(((TBTreeDatabase*)aDB), aKey, aKeySize, aValueSize);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

int tcDBGetValueSize(void *aDB, const char *aKey, int aKeySize){
  assert(aDB && aKey && aKeySize >= 0);
  if(!DBLOCKMETHOD(aDB, false)) return -1;
  if(!((TBTreeDatabase*)aDB)->Opened){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return -1;
  }
  int vResult = tcbdbvsiz(((TBTreeDatabase*)aDB)->DB, aKey, aKeySize);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

int tcDBIndexGetValueSize(void *aDB, const char *aKey, int aKeySize){
  assert(aDB && aKey && aKeySize >= 0);
  return tcbdbvsiz(((TBTreeDatabase*)aDB)->IndexDB, aKey, aKeySize);
}

int tcDBConfigGetValueSize(void *aDB, const char *aKey, int aKeySize){
  assert(aDB && aKey && aKeySize >= 0);
  return tchdbvsiz(((TBTreeDatabase*)aDB)->ConfigDB, aKey, aKeySize);
}

bool tcDBIterInit(void *aDB) {
  assert(aDB);
  if(!DBLOCKMETHOD(aDB, false)) return NULL;
  if(((TBTreeDatabase*)aDB)->DB == NULL){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  bool vResult = true;
  if(!tcbdbcurfirst(((TBTreeDatabase*)aDB)->Cursor)){
    int ecode = tcbdbecode(((TBTreeDatabase*)aDB)->DB);
    if(ecode != TCESUCCESS && ecode != TCEINVALID && ecode != TCEKEEP && ecode != TCENOREC)
      vResult = false;
  }
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

void* tcDBIterNext(void *aDB, int *aSize){
  assert(aDB && aSize);
  if(!DBLOCKMETHOD(aDB, false)) return NULL;
  if(((TBTreeDatabase*)aDB)->DB == NULL){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return NULL;
  }
  char *vResult = tcbdbcurkey(((TBTreeDatabase*)aDB)->Cursor, aSize);
  tcbdbcurnext(((TBTreeDatabase*)aDB)->Cursor);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

TCLIST *tcDBGetMatchedKeys(void *aDB, const char *aKey, int aKeySize, int aMaxCount) {
  assert(aDB && aKey && aKeySize >= 0);
  if(!DBLOCKMETHOD(aDB, false)) return tclistnew();
  if(((TBTreeDatabase*)aDB)->DB == NULL){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return tclistnew();
  }
  TCLIST *vResult = tcbdbfwmkeys(((TBTreeDatabase*)aDB)->DB, aKey, aKeySize, aMaxCount);
  DBUNLOCKMETHOD(aDB);

  return vResult;
}

int tcDBAddInt(void *aDB, const char *aKey, int aKeySize, int aNumber){
  assert(aDB && aKey && aKeySize >= 0);
  return tcbdbaddint(((TBTreeDatabase*)aDB)->DB, aKey, aKeySize, aNumber);
}

double tcDBAddDouble(void *aDB, const char *aKey, int aKeySize, double aNumber){
  assert(aDB && aKey && aKeySize >= 0);
  return tcbdbadddouble(((TBTreeDatabase*)aDB)->DB, aKey, aKeySize, aNumber);
}

bool tcDBOptimizeImpl(void *aDB, const char *aParams){
  assert(aDB);
  TCLIST *elems = aParams ? tcstrsplit(aParams, "#") : tclistnew();
  int64_t bnum = -1;
  int64_t capnum = -1;
  int64_t capsiz = -1;
  int8_t apow = -1;
  int8_t fpow = -1;
  bool tdefault = true;
  bool tlmode = false;
  bool tdmode = false;
  bool tbmode = false;
  bool ttmode = false;
  int32_t lmemb = -1;
  int32_t nmemb = -1;
  int32_t width = -1;
  int64_t limsiz = -1;
  int ln = TCLISTNUM(elems);
  for(int i = 0; i < ln; i++){
    const char *elem = TCLISTVALPTR(elems, i);
    char *pv = strchr(elem, '=');
    if(!pv) continue;
    *(pv++) = '\0';
    if(!tcstricmp(elem, "bnum")){
      bnum = tcatoix(pv);
    } else if(!tcstricmp(elem, "capnum")){
      capnum = tcatoix(pv);
    } else if(!tcstricmp(elem, "capsiz")){
      capsiz = tcatoix(pv);
    } else if(!tcstricmp(elem, "apow")){
      apow = tcatoix(pv);
    } else if(!tcstricmp(elem, "fpow")){
      fpow = tcatoix(pv);
    } else if(!tcstricmp(elem, "opts")){
      tdefault = false;
      if(strchr(pv, 'l') || strchr(pv, 'L')) tlmode = true;
      if(strchr(pv, 'd') || strchr(pv, 'D')) tdmode = true;
      if(strchr(pv, 'b') || strchr(pv, 'B')) tbmode = true;
      if(strchr(pv, 't') || strchr(pv, 'T')) ttmode = true;
    } else if(!tcstricmp(elem, "lmemb")){
      lmemb = tcatoix(pv);
    } else if(!tcstricmp(elem, "nmemb")){
      nmemb = tcatoix(pv);
    } else if(!tcstricmp(elem, "width")){
      width = tcatoix(pv);
    } else if(!tcstricmp(elem, "limsiz")){
      limsiz = tcatoix(pv);
    }
  }
  tclistdel(elems);

  bool vResult = true;
  int opts;
  opts = 0;
  if(tdefault){
    opts = UINT8_MAX;
  } else {
    if(tlmode) opts |= BDBTLARGE;
    if(tdmode) opts |= BDBTDEFLATE;
    if(tbmode) opts |= BDBTBZIP;
    if(ttmode) opts |= BDBTTCBS;
  }
  if(!tcbdboptimize(((TBTreeDatabase*)aDB)->DB, lmemb, nmemb, bnum, apow, fpow, opts)) vResult = false;
  if(!tcbdboptimize(((TBTreeDatabase*)aDB)->IndexDB, lmemb, nmemb, bnum, apow, fpow, opts)) vResult = false;
  //if(!tchdboptimize(((TBTreeDatabase*)aDB)->ConfigDB, bnum, apow, fpow, opts)) vResult = false;
  return vResult;
}

bool tcDBOptimize(void *aDB, const char *aParams){
  assert(aDB);
  if(!DBLOCKMETHOD(aDB, true)) return false;
  if(!((TBTreeDatabase*)aDB)->Opened || !((TBTreeDatabase*)aDB)->Writable || ((TBTreeDatabase*)aDB)->InTransaction){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  DBTHREADYIELD(aDB);
  bool vResult = tcDBOptimizeImpl(aDB, aParams);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

bool tcDBClear(void *aDB){
  assert(aDB);
  if(!DBLOCKMETHOD(aDB, true)) return false;
  if(!((TBTreeDatabase*)aDB)->Opened || !((TBTreeDatabase*)aDB)->Writable || ((TBTreeDatabase*)aDB)->InTransaction){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  DBTHREADYIELD(aDB);
  bool vResult = tcbdbvanish(((TBTreeDatabase*)aDB)->DB);
  if (!tcbdbvanish(((TBTreeDatabase*)aDB)->IndexDB)) {
    vResult = false;
  }
  if (!tchdbvanish(((TBTreeDatabase*)aDB)->ConfigDB)) {
    vResult = false;
  }
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

bool tcDBCopyImpl(TBTreeDatabase *aDB, const char *aPath){
  bool vResult = tcbdbcopy(aDB->DB, aPath);
  TCXSTR *vPath = tcxstrnew2(aPath);
  tcxstrcat(vPath, IDXEXTNAME, strlen(IDXEXTNAME));

  if (!tcbdbcopy(aDB->IndexDB, vPath->ptr)) {
    vResult = false;
  }
  
  strcpy(vPath->ptr, aPath);
  strncat(vPath->ptr, CFGEXTNAME, strlen(CFGEXTNAME));
  if (!tchdbcopy(aDB->ConfigDB, vPath->ptr)) {
    vResult = false;
  }
  tcxstrdel(vPath);
  return vResult;
}

bool tcDBCopy(void *aDB, const char *aPath){
  assert(aDB);
  if(!DBLOCKMETHOD(aDB, true)) return false;
  if(!((TBTreeDatabase*)aDB)->Opened){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  DBTHREADYIELD(aDB);
  bool vResult = tcDBCopyImpl((TBTreeDatabase*)aDB, aPath);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

bool tcDBTransBeginImpl(TBTreeDatabase* aDB){
  assert(aDB);
  if (!tcbdbtranbegin(aDB->DB)) return false;
  bool vResult = true;
  if (!tcbdbtranbegin(aDB->IndexDB)) {
    tcDBRaiseError(aDB, tcbdbecode(aDB->IndexDB),  __FILE__, __LINE__, __func__);
    vResult = false;
  }
  if (!tchdbtranbegin(aDB->ConfigDB)) {
    tcDBRaiseError(aDB, tchdbecode(aDB->ConfigDB),  __FILE__, __LINE__, __func__);
    vResult = false;
  }
  return vResult;
}

bool tcDBTransBegin(void *aDB){
  assert(aDB);
  TBTreeDatabase* vDB = (TBTreeDatabase*) aDB;
//  printf("begin trans:");
//  if (vDB->InTransaction) printf("true\n");
  for(double wsec = 1.0 / sysconf(_SC_CLK_TCK); true; wsec *= 2){
    if(!DBLOCKMETHOD(aDB, true)) return false;
    if(!vDB->Opened || !vDB->Writable){
      tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
      DBUNLOCKMETHOD(aDB);
      return false;
    }
    if(!vDB->InTransaction) break;
    DBUNLOCKMETHOD(aDB);
    if(wsec > 1.0) wsec = 1.0;
    tcsleep(wsec);
  }

  if(!tcDBTransBeginImpl(vDB)){
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  vDB->InTransaction = true;
  DBUNLOCKMETHOD(aDB);
  return true;
}

bool tcDBTransCommitImpl(TBTreeDatabase* aDB){
  bool vResult = tcbdbtrancommit(aDB->DB);
  if (!tcbdbtrancommit(aDB->IndexDB)) {
    vResult = false;
  }
  if (!tchdbtrancommit(aDB->ConfigDB)) {
    vResult = false;
  }
  return vResult;
}

bool tcDBTransCommit(void *aDB){
  assert(aDB);
  if(!DBLOCKMETHOD(aDB, true)) return false;
  TBTreeDatabase* vDB = (TBTreeDatabase*) aDB;
  if(!vDB->Opened || !vDB->Writable){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  vDB->InTransaction = false;
  bool vResult = tcDBTransCommitImpl((TBTreeDatabase*)aDB);  
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

static bool tcDBTransAbortImpl(TBTreeDatabase *aDB) {
  bool vResult = tcbdbtranabort(aDB->DB) && tcbdbtranabort(aDB->IndexDB) && tchdbtranabort(aDB->ConfigDB);
  aDB->InTransaction = false;
  return vResult;
}

bool tcDBTransAbort(void *aDB) {
  assert(aDB);
  if(!DBLOCKMETHOD(aDB, true)) return false;
  if(!((TBTreeDatabase*)aDB)->Opened || !((TBTreeDatabase*)aDB)->Writable || !((TBTreeDatabase*)aDB)->InTransaction){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  bool vResult = tcDBTransAbortImpl((TBTreeDatabase*)aDB);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

const char *tcDBPath(void *aDB){
  assert(aDB);
  return tcbdbpath(((TBTreeDatabase*)aDB)->DB);
}

uint64_t tcDBSize(void *aDB){
  assert(aDB);
  if(!DBLOCKMETHOD(aDB, false)) return 0;
  if(!((TBTreeDatabase*)aDB)->Opened){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return 0;
  }
  uint64_t vResult = tcbdbfsiz(((TBTreeDatabase*)aDB)->DB) + tcbdbfsiz(((TBTreeDatabase*)aDB)->IndexDB) + tchdbfsiz(((TBTreeDatabase*)aDB)->ConfigDB);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

bool tcDBSync(void *aDB) {
  assert(aDB);
  if(!DBLOCKMETHOD(aDB, true)) return false;
  if(!((TBTreeDatabase*)aDB)->Opened || !((TBTreeDatabase*)aDB)->Writable || ((TBTreeDatabase*)aDB)->InTransaction){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  bool vResult = tcDBMemSync(aDB, true);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

bool tcDBReplaceProcDup(void *aDB, const void *aKey, int aKeySize, const void *aValue, int aValueSize,
                            TCPDPROC aProc, void *aProcParams){
  assert(aDB && aKey && aKeySize >= 0 && aProc);
  return tcbdbputproc(((TBTreeDatabase*)aDB)->DB, aKey, aKeySize, aValue, aValueSize, aProc, aProcParams);
}

bool tcDBForEach(void *aDB, TCITER aIterProc, void *aIterParams) {
  assert(aDB && aIterProc);
  return tcbdbforeach(((TBTreeDatabase*)aDB)->DB, aIterProc, aIterParams);
}

bool tcDBOpen(void *aDB, const char *aName){
  assert(aDB && aName);
  if(!DBLOCKMETHOD(aDB, true)) return false;
  if(((TBTreeDatabase*) aDB)->Opened){ //already open
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  bool rv = tcDBOpenImpl((TBTreeDatabase *) aDB, aName);
  DBUNLOCKMETHOD(aDB);
  return rv;
  
}

static bool tcDBUpdateFrom2To2_1(TBTreeDatabase *aDB) {
  return true;
}

static bool tcDBUpdateDBVersionImpl(TBTreeDatabase *aDB) {
  assert(aDB);
  float vDBVersion = 0.0;
  bool vIsCurrent = false;
  printf("DBVer:%f", vDBVersion);
  tchdbget3(aDB->ConfigDB, DBVERCFGKEY, strlen(DBVERCFGKEY), &vDBVersion, sizeof(vDBVersion));
  printf("DBVer:%f", vDBVersion);
  vIsCurrent = vDBVersion == _TCSKELDB_PROTVER;
  if (!vIsCurrent) {
    uint64_t vRecCount = 0;
    if (tchdbget3(aDB->ConfigDB, REC_COUNTCFGKEY, strlen(REC_COUNTCFGKEY), &vRecCount, sizeof(vRecCount)) != -1 && vRecCount != 0) {
      if (tcDBUpdateFrom2To2_1(aDB)) {
        vDBVersion = _TCSKELDB_PROTVER;
        vIsCurrent = tchdbput(aDB->ConfigDB, REC_COUNTCFGKEY, strlen(REC_COUNTCFGKEY), &vDBVersion, sizeof(vDBVersion)); 
      }
    }
    else {
      vDBVersion = _TCSKELDB_PROTVER;
      vIsCurrent = tchdbput(aDB->ConfigDB, REC_COUNTCFGKEY, strlen(REC_COUNTCFGKEY), &vDBVersion, sizeof(vDBVersion)); 
    }

    
  }
  return vIsCurrent;
}

static bool tcDBOpenImpl(TBTreeDatabase *aDB, const char *aName) {  
  assert(aDB && aName);

  TCLIST *elems = tcstrsplit(aName, "#");
  char *path = tclistshift2(elems);
  if(!path){
    tclistdel(elems);
    return false;
  }
  int dbgfd = -1;
  int64_t bnum = -1;
  int64_t capnum = -1;
  int64_t capsiz = -1;
  bool owmode = true;
  bool ocmode = true;
  bool otmode = false;
  bool onlmode = false;
  bool onbmode = false;
  int8_t apow = -1;
  int8_t fpow = -1;
  bool tlmode = false;
  bool tdmode = false;
  bool tbmode = false;
  bool ttmode = false;
  int32_t rcnum = -1;
  int64_t xmsiz = -1;
  int32_t dfunit = -1;
  int32_t lmemb = -1;
  int32_t nmemb = -1;
  int32_t lcnum = -1;
  int32_t ncnum = -1;
  int32_t width = -1;
  int64_t limsiz = -1;
  TCLIST *idxs = NULL;
  int ln = TCLISTNUM(elems);
  for(int i = 0; i < ln; i++){
    const char *elem = TCLISTVALPTR(elems, i);
    char *pv = strchr(elem, '=');
    if(!pv) continue;
    *(pv++) = '\0';
    if(!tcstricmp(elem, "dbgfd")){
      dbgfd = tcatoi(pv);
    } else if(!tcstricmp(elem, "bnum")){
      bnum = tcatoix(pv);
    } else if(!tcstricmp(elem, "capnum")){
      capnum = tcatoix(pv);
    } else if(!tcstricmp(elem, "capsiz")){
      capsiz = tcatoix(pv);
    } else if(!tcstricmp(elem, "mode")){
      owmode = strchr(pv, 'w') || strchr(pv, 'W');
      ocmode = strchr(pv, 'c') || strchr(pv, 'C');
      otmode = strchr(pv, 't') || strchr(pv, 'T');
      onlmode = strchr(pv, 'e') || strchr(pv, 'E');
      onbmode = strchr(pv, 'f') || strchr(pv, 'F');
    } else if(!tcstricmp(elem, "apow")){
      apow = tcatoix(pv);
    } else if(!tcstricmp(elem, "fpow")){
      fpow = tcatoix(pv);
    } else if(!tcstricmp(elem, "opts")){
      if(strchr(pv, 'l') || strchr(pv, 'L')) tlmode = true;
      if(strchr(pv, 'd') || strchr(pv, 'D')) tdmode = true;
      if(strchr(pv, 'b') || strchr(pv, 'B')) tbmode = true;
      if(strchr(pv, 't') || strchr(pv, 'T')) ttmode = true;
    } else if(!tcstricmp(elem, "rcnum")){
      rcnum = tcatoix(pv);
    } else if(!tcstricmp(elem, "xmsiz")){
      xmsiz = tcatoix(pv);
    } else if(!tcstricmp(elem, "dfunit")){
      dfunit = tcatoix(pv);
    } else if(!tcstricmp(elem, "lmemb")){
      lmemb = tcatoix(pv);
    } else if(!tcstricmp(elem, "nmemb")){
      nmemb = tcatoix(pv);
    } else if(!tcstricmp(elem, "lcnum")){
      lcnum = tcatoix(pv);
    } else if(!tcstricmp(elem, "ncnum")){
      ncnum = tcatoix(pv);
    } else if(!tcstricmp(elem, "width")){
      width = tcatoix(pv);
    } else if(!tcstricmp(elem, "limsiz")){
      limsiz = tcatoix(pv);
    } else if(!tcstricmp(elem, "idx")){
      if(!idxs) idxs = tclistnew();
      TCLISTPUSH(idxs, pv, strlen(pv));
    } 
  }
  tclistdel(elems);

  //if(tcstribwm(path, ".tcb") || tcstribwm(path, ".bdb")){
  //register TBTreeDatabase *vDB = aDB;
  TCBDB *bdb = aDB->DB;//tcbdbnew();
  if(dbgfd >= 0) tcbdbsetdbgfd(bdb, dbgfd);
  tcbdbsetmutex(bdb);
  int opts = 0;
  if(tlmode) opts |= BDBTLARGE;
  if(tdmode) opts |= BDBTDEFLATE;
  if(tbmode) opts |= BDBTBZIP;
  if(ttmode) opts |= BDBTTCBS;
  tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts);
  tcbdbsetcache(bdb, lcnum, ncnum);
  //if (xmsiz < 0) xmsiz = getAvailablePhysMemory();
  if(xmsiz >= 0) tcbdbsetxmsiz(bdb, xmsiz); 
  if(dfunit >= 0) tcbdbsetdfunit(bdb, dfunit);
  if(capnum > 0) tcbdbsetcapnum(bdb, capnum);
  int omode = owmode ? BDBOWRITER : BDBOREADER;
  if(ocmode) omode |= BDBOCREAT;
  if(otmode) omode |= BDBOTRUNC;
  if(onlmode) omode |= BDBONOLCK;
  if(onbmode) omode |= BDBOLCKNB;
  if(!tcbdbopen(bdb, path, omode)){
    tcbdbdel(bdb);
    TCFREE(path);
    if(idxs) tclistdel(idxs);
    return false;
  }
  //aDB->DB = bdb;
  aDB->Cursor = tcbdbcurnew(bdb);
  
  TCXSTR *vPath = tcxstrnew2(path);
  //Index Data File 
  bdb = aDB->IndexDB;//tcbdbnew();
  if(dbgfd >= 0) tcbdbsetdbgfd(bdb, dbgfd);
  tcbdbsetmutex(bdb);
//  opts = 0;
//  if(tlmode) opts |= BDBTLARGE;
//  if(tdmode) opts |= BDBTDEFLATE;
//  if(tbmode) opts |= BDBTBZIP;
//  if(ttmode) opts |= BDBTTCBS;
  tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts);
  tcbdbsetcache(bdb, lcnum, ncnum);
//  if (xmsiz < 0) xmsiz = getAvailablePhysMemory();
  if(xmsiz >= 0) tcbdbsetxmsiz(bdb, xmsiz >> 1);
  if(dfunit >= 0) tcbdbsetdfunit(bdb, dfunit);
  if(capnum > 0) tcbdbsetcapnum(bdb, capnum);
  omode = owmode ? BDBOWRITER : BDBOREADER;
  if(ocmode) omode |= BDBOCREAT;
  if(otmode) omode |= BDBOTRUNC;
  if(onlmode) omode |= BDBONOLCK;
  if(onbmode) omode |= BDBOLCKNB;
  tcxstrcat(vPath, IDXEXTNAME, strlen(IDXEXTNAME));
  if(!tcbdbopen(bdb, vPath->ptr, omode)){
    tcbdbdel(bdb);
    TCFREE(path);
    tcxstrdel(vPath);
    if(idxs) tclistdel(idxs);
    return false;
  }
  //aDB->IndexDB = bdb;

  //DB Config Data File
  TCHDB *hdb = aDB->ConfigDB; //tchdbnew();
  if(dbgfd >= 0) tchdbsetdbgfd(hdb, dbgfd);
  tchdbsetmutex(hdb);
  opts = 0;
  if(tlmode) opts |= HDBTLARGE;
  if(tdmode) opts |= HDBTDEFLATE;
  if(tbmode) opts |= HDBTBZIP;
  if(ttmode) opts |= HDBTTCBS;
  tchdbtune(hdb, bnum, apow, fpow, opts);
  tchdbsetcache(hdb, rcnum);
  if(xmsiz >= 0) tchdbsetxmsiz(hdb, xmsiz >> 4);
  if(dfunit >= 0) tchdbsetdfunit(hdb, dfunit);
  omode = owmode ? HDBOWRITER : HDBOREADER;
  if(ocmode) omode |= HDBOCREAT;
  if(otmode) omode |= HDBOTRUNC;
  if(onlmode) omode |= HDBONOLCK;
  if(onbmode) omode |= HDBOLCKNB;
  strcpy(vPath->ptr, path);
  strcat(vPath->ptr, CFGEXTNAME);
  if(!tchdbopen(hdb, vPath->ptr, omode)){
    tchdbdel(hdb);
    if(idxs) tclistdel(idxs);
    TCFREE(path);
    return false;
  }

  //aDB->ConfigDB = hdb;
  if(idxs){
    int xnum = TCLISTNUM(idxs);
    for(int i = 0; i < xnum; i++){
      const char *expr = TCLISTVALPTR(idxs, i);
      int type = TDBITLEXICAL;
      char *pv = strchr(expr, ':');
      if(pv){
        *(pv++) = '\0';
        type = tctdbstrtoindextype(pv);
      }
      if(type >= 0) tcDBIndexSet(aDB, expr, strlen(expr), pv, strlen(pv), type | TDBITKEEP);
    }
    tclistdel(idxs);
  }
  tcxstrdel(vPath);
  //}
  TCFREE(path);
  aDB->Writable = owmode;
  aDB->Opened = true;

  return true;
  //return tcDBUpdateDBVersionImpl(aDB);
}

static inline uint64_t tcBDBRangeCount(TCBDB *adb, char *kbuf, int ksiz){
  uint64_t vResult = 0;
  TCLIST *vList = NULL;
  char vLastKeyBuf[MAXKEYLEN+1], vEndkbuf[MAXKEYLEN+1];
  int vLastKeySiz = ksiz, vEndksiz = ksiz;
  strcpy(vLastKeyBuf, kbuf);
  strcpy(vEndkbuf, kbuf);
  vEndkbuf[vEndksiz-1] = vEndkbuf[vEndksiz-1] + 1;
  int vCount;
  do {
    vList = tcbdbrange(adb, vLastKeyBuf, vLastKeySiz, false, vEndkbuf, vEndksiz, false, MAXLISTSIZE);
    if (vList) vCount = TCLISTNUM(vList); else vCount = 0;
    if (vCount > 0) {
      vResult = vResult + vCount;
      TCLISTVAL(kbuf, vList, vCount - 1, ksiz);
      vLastKeySiz = ksiz;
      strcpy(vLastKeyBuf, kbuf);
    }
    if (vList) tclistdel(vList);
  } while (vCount > 0);
  return vResult;
}

uint64_t tcDBGetIndexNum(TBTreeDatabase *aDB){
  assert(aDB);
  return tcbdbrnum(aDB->IndexDB);//tcBDBRangeCount(aDB->IndexDB, IDXKEY, strlen(IDXKEY));
}

uint64_t tcDBGetConfigNum(TBTreeDatabase *aDB){
  assert(aDB);
  return tchdbrnum(aDB->ConfigDB); //tcBDBRangeCount(aDB->ConfigDB, IDXCONFIGKEY, strlen(IDXCONFIGKEY));
}

static inline uint64_t tcDBGetFieldIndexCountImpl(TBTreeDatabase *aDB, const char *aFieldName, const int aFieldNameSize){
  uint64_t vResult;
  char vIdxCountCfg[MAXKEYLEN+1] = IDXCOUNTCONFIGKEY;
  char *vP = vIdxCountCfg;
  vP += strlen(IDXCONFIGKEY);
  memcpy(vP, aFieldName, aFieldNameSize);
  vP += aFieldNameSize;
  if (tchdbget3(aDB->ConfigDB, vIdxCountCfg, vP-vIdxCountCfg, &vResult, sizeof(vResult)) < 0)
    vResult = 0;
  return vResult;
}

static inline uint64_t tcDBGetRecordNumImpl(TBTreeDatabase *aDB){
  uint64_t vResult;
  if (tchdbget3(aDB->ConfigDB, REC_COUNTCFGKEY, strlen(REC_COUNTCFGKEY), &vResult, sizeof(vResult)) < 0)
    vResult = 0;
  return vResult;
}

uint64_t tcDBGetRecordNum(void *aDB){
  assert(aDB);
  if(!DBLOCKMETHOD(aDB, false)) return 0;
  if(!((TBTreeDatabase*) aDB)->Opened){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return 0;
  }
  //uint64_t vResult = tcbdbrnum(adb->bdb) - tcBDBRangeCount(adb->bdb, IDXKEY, strlen(IDXKEY));
  //fprintf(stderr, "rnum:%d\n", vResult);
  //uint64_t vResult = tcbdbrnum(((TBTreeDatabase*)aDB)->DB);
  //uint64_t vResult = tcDBGetMatchedKeysCount(((TBTreeDatabase*)aDB), KEYSEPSTR, strlen(KEYSEPSTR));
  uint64_t vResult = tcDBGetRecordNumImpl((TBTreeDatabase*) aDB);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

bool tcDBIndexRemove(void *aDB, const char *aFieldbuf, int aFieldSiz) {
  assert(aDB && aFieldbuf);
  if(!DBLOCKMETHOD(aDB, true)) {
#ifdef DEBUG
    fprintf(stderr, "DBIndexRemove(%s) Error: method locked.\n", aFieldbuf);
#endif
    return false;
  }
  if(!((TBTreeDatabase*)aDB)->Opened || !((TBTreeDatabase*)aDB)->Writable || ((TBTreeDatabase*)aDB)->InTransaction){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  bool vResult;
  char vIdxName[MAXKEYLEN+1];
  strcpy(vIdxName, IDXCONFIGKEY);
  strncat(vIdxName, aFieldbuf, aFieldSiz);
  vResult = tchdbout2(((TBTreeDatabase*)aDB)->ConfigDB, vIdxName);
  if (vResult) {
    vResult = tcBDBIndexRemove(((TBTreeDatabase*)aDB)->IndexDB, aFieldbuf, aFieldSiz);
  }
#ifdef DEBUG
  else {
    fprintf(stderr, "DBIndexRemove(%s) Error: can not delete index from Config DB.\n", aFieldbuf);    
  }
#endif
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

/* Synchronize updating contents on memory of a table database object. */
bool tcDBMemSync(void *aDB, bool phys){
  assert(aDB);
  if(!((TBTreeDatabase*)aDB)->Opened || !((TBTreeDatabase*)aDB)->Writable){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    return false;
  }
  bool vResult = true;
  if(!tcbdbmemsync(((TBTreeDatabase*)aDB)->DB, phys)) vResult = false;
  if(!tchdbmemsync(((TBTreeDatabase*)aDB)->ConfigDB, phys)) vResult = false;
  if(!tcbdbmemsync(((TBTreeDatabase*)aDB)->IndexDB, phys)) vResult = false;
  return vResult;
}

/* JSON -----
 Return the Parsed string if success. and the aValue will be skiped the position of the string.
 u need free the string to dispose the resource.
 NOte: CHAR MUST use the '', string/array use the "" in C.
 */
/*
 static const char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
 static const char *parse_string(const char **aValue){
 const char vSep = **aValue;
 const char *ptr=*aValue+1;char *ptr2;char *out;int len=0;unsigned uc;
 //aStrLen = 0;
 if (vSep != '\'' && vSep != '"') return 0; //not a string.
 
 while (*ptr!=vSep && (unsigned char)*ptr>31 && ++len) if (*ptr++ == '\\') ptr++;	// Skip escaped quotes.
 
 //fprintf(stderr,"l:%d\n",len);
 //aValue = skip(aValue+1);
 //fprintf(stderr,"sl:%d\n",len);
 out = (char*)malloc(len+1);	// This is how long we need for the string, roughly.
 //fprintf(stderr,"l:%d\n",len);
 if (!out) return 0;
 
 ptr=*aValue + 1;ptr2=out;
 while ( *ptr!=vSep && (unsigned char)*ptr>31)
 {
 if (*ptr!='\\') *ptr2++=*ptr++;
 else
 {
 ptr++;
 switch (*ptr)
 {
 case 'b': *ptr2++='\b';	break;
 case 'f': *ptr2++='\f';	break;
 case 'n': *ptr2++='\n';	break;
 case 'r': *ptr2++='\r';	break;
 case 't': *ptr2++='\t';	break;
 case 'u':	 // transcode utf16 to utf8. DOES NOT SUPPORT SURROGATE PAIRS CORRECTLY.
 sscanf(ptr+1,"%4x",&uc);	// get the unicode char.
 len=3;if (uc<0x80) len=1;else if (uc<0x800) len=2;ptr2+=len;
 
 switch (len) {
 case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
 case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
 case 1: *--ptr2 =(uc | firstByteMark[len]);
 }
 ptr2+=len;ptr+=4;
 break;
 default:  *ptr2++=*ptr; break;
 }
 ptr++;
 }
 }
 *ptr2=0;
 if (*ptr==vSep) ptr++;
 *aValue = ptr;
 return out;
 }
 //*/
bool tcBDBIndexRemove(TCBDB *aDB, const char *aFieldbuf, int aFieldSiz) {
  bool vResult = true;
#if defined(DEBUG)
  fprintf(stderr, "begin remove index %s .\n", aFieldbuf);
#endif
  
    char vIdxName[MAXKEYLEN+1];
    strcpy(vIdxName, IDXKEY);
    strncat(vIdxName, aFieldbuf, aFieldSiz);
    strcat(vIdxName, "/");
    //vResult = tcbdbout3(aDB, vIdxName, strlen(vIdxName));
    TCLIST *vList = tcbdbfwmkeys2(aDB, vIdxName, MAXLISTSIZE);
    int vCount = 0;
    if (vList) vCount = tclistnum(vList);
    while (vCount > 0 && vResult) {
      //vCount--;
      for (int i= 0; i < vCount; i++) {
        const char *kbuf;
        int ksiz;
        TCLISTVAL(kbuf, vList, i, ksiz);
        vResult = tcbdbout3(aDB, kbuf, ksiz);
        if (!vResult) {
#if defined(DEBUG)
          fprintf(stderr, "remove the index %s failed: %s.\n", kbuf, tcbdberrmsg(tcbdbecode(aDB)));
#endif
          tcbdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
          break;
        }
      }
      if (vResult) {
        tclistdel(vList);
        vList = tcbdbfwmkeys2(aDB, vIdxName, MAXLISTSIZE);
        if (vList) vCount = tclistnum(vList); else vCount = 0;
      }
    } 
    if (vList) tclistdel(vList);
    if (vResult) {
    }
    else {
#if defined(DEBUG)
      fprintf(stderr, "remove Index %s failed: %s.\n", vIdxName, tcbdberrmsg(tcbdbecode(aDB)));
#endif
      tcbdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    }
    
  //if (vResult) fprintf(stderr, "remove index %s ok.\n", aFieldbuf);
  return vResult;
}

//recreate the index of the Field.
bool tcDBReIndexImpl(TBTreeDatabase *aDB, const char *aFieldbuf, int aFieldSiz, const char *aOptions, int aOptionsSiz, int aOptionInt){
  bool vResult = true;
  
  char vFieldName[MAXKEYLEN+1];
  strcpy(vFieldName, FIELDSEPSTR);
  strncat(vFieldName, aFieldbuf, aFieldSiz);
  int vFieldSize = strlen(FIELDSEPSTR)+aFieldSiz;
  //    strcpy(vIdxName, IDXKEY);
    //    strncat(vIdxName, aFieldbuf, aFieldSiz);
    //    strcat(vIdxName, "/");
  TCLIST *vList = tcbdbrange(aDB->DB, NULL, 0, true, NULL, 0, true, MAXLISTSIZE);
  int vCount = 0;
  if (vList) vCount = tclistnum(vList);
  while (vCount > 0) {
      const char *kbuf, *vbuf;
      int ksiz, vsiz;
      for (int i= 0; i < vCount; i++) {//through out the fields:
        TCLISTVAL(kbuf, vList, i, ksiz);
        if (kbuf[0] == KEYSEPCHAR) break;
//        if (isKeepReservedChar(kbuf)) {
//          continue;
//        }
#if defined(DEBUG)
        //fprintf(stderr, "reindex the index %s i:%d.\n", kbuf, i);
#endif
        
        if (strncmp(&kbuf[ksiz-strlen(FIELDSEPSTR)-aFieldSiz], vFieldName, vFieldSize) == 0) { //that should be an index
          vbuf = tcbdbget2(aDB->DB, kbuf);
          vsiz = strlen(vbuf);
          //printf("enter ok:%s\n", kbuf+ksiz-strlen(FIELDSEPSTR)-aFieldSiz);
          if (!tcDBPutFieldIndexImpl(aDB, kbuf, ksiz, vbuf, vsiz)) vResult = false;
#if defined(DEBUG)
          if (vResult) fprintf(stderr, "reindex the index %s i:%d.\n", kbuf, i);
#endif
          //printf("result:%d\n", vResult);
        }
        
#if defined(DEBUG)
        //        fprintf(stderr, "reindex the index %s end.\n", kbuf);
#endif
      } //for structure-value through out field
//      if (vResult) {
    if (kbuf[0] == KEYSEPCHAR) break;
        TCLIST *t = vList;
        vList = tcbdbrange(aDB->DB, kbuf, ksiz, false, NULL, 0, true, MAXLISTSIZE);
        tclistdel(t);
        if (vList) {
          vCount = tclistnum(vList);
#if defined(DEBUG)
          //          fprintf(stderr, "Find the count %d.\n", vCount);
#endif
        }
        else vCount = 0;
//      }
//      else {
//        break;
//      }
      
    } //while 
    if (vList) tclistdel(vList);
  
  return vResult;  
}

bool tcDBReIndex(TBTreeDatabase *aDB, const char *aFieldbuf, int aFieldSiz, const char *aOptions, int aOptionsSiz, int aOptionInt){
  if(!DBLOCKMETHOD(aDB, true)) return false;
  if(!((TBTreeDatabase*)aDB)->Opened || !((TBTreeDatabase*)aDB)->Writable || ((TBTreeDatabase*)aDB)->InTransaction){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  bool vResult = tcDBReIndexImpl(aDB, aFieldbuf, aFieldSiz, aOptions, aOptionsSiz, aOptionInt);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

bool tcDBIndexSet(TBTreeDatabase *aDB, const char *aFieldbuf, int aFieldSiz, const char *aOptions, int aOptionsSiz, int aOptionInt){
  if(!DBLOCKMETHOD(aDB, true)) return false;
  if(!((TBTreeDatabase*)aDB)->Opened || !((TBTreeDatabase*)aDB)->Writable || ((TBTreeDatabase*)aDB)->InTransaction){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  bool vResult;
  char vIdxName[MAXKEYLEN+1];
  char *vP = vIdxName;
  memcpy(vP, IDXCONFIGKEY, strlen(IDXCONFIGKEY));
  vP += strlen(IDXCONFIGKEY);
  memcpy(vP, aFieldbuf, aFieldSiz);
  vP += aFieldSiz;
  //  if (aOptionInt == -1) {
  //    aOptionInt = tctdbstrtoindextype(aOptions);
  //  }
  //  vResult = tcDBReIndex(aDB, aFieldbuf, aFieldSiz, aOptions, aOptionsSiz, aOptionInt);
  //  if (vResult) {
  //vResult = tchdbput(aDB->ConfigDB, vIdxName, vP-vIdxName, aOptions, aOptionsSiz);
  aOptionInt+=1;
  vResult = tchdbput(aDB->ConfigDB, vIdxName, vP-vIdxName, &aOptionInt, sizeof(aOptionInt));
  //  }
  DBUNLOCKMETHOD(aDB);
  return vResult;
}



static inline void tcxstrcatKeyField(TCXSTR *aValue, const char *kbuf, const int ksiz, const char *aFieldKey) {
  tcxstrclear(aValue);
  TCXSTRCAT(aValue, FIELDSEPSTR, strlen(FIELDSEPSTR));
  TCXSTRCAT(aValue, kbuf, ksiz);
  TCXSTRCAT(aValue, FIELDSEPSTR, strlen(FIELDSEPSTR));
  tcxstrcat2(aValue, aFieldKey);            
} 

static bool tcDBDeleteFieldIndexImpl(TBTreeDatabase *aDB, const char *kbuf, int ksiz) {
  bool vResult = false;
  
  char vIdxName[MAXKEYLEN+1];
  int vIsIndex;
  //a single field storage.
  const char *vFieldName = strrchr(kbuf, FIELDSEPCHAR); 
  if ((vFieldName != NULL) && (vFieldName-kbuf-2 > 0)) {
    vFieldName++;
    int vFieldNameSize = ksiz - (vFieldName-kbuf);
    if ((strlen(IDXCONFIGKEY) + vFieldNameSize < MAXKEYLEN)) {
      strcpy(vIdxName, IDXCONFIGKEY);
      strcat(vIdxName, vFieldName);
      vIsIndex = tchdbvsiz(aDB->ConfigDB, vIdxName, strlen(IDXCONFIGKEY)+vFieldNameSize);
      if (vIsIndex >= 0) { //it is the index field, need to delete the index.
        int vsiz;
        const char *vbuf = tcbdbget(aDB->DB, kbuf, ksiz, &vsiz);
        char *vP = vIdxName;
        //vIdxName = IDXKEY;
        memcpy(vP, IDXKEY, strlen(IDXKEY));
        vP += strlen(IDXKEY);
        memcpy(vP, vFieldName, vFieldNameSize);
        vP += vFieldNameSize;
        memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR));
        vP += strlen(KEYSEPSTR);
        memcpy(vP, vbuf, vsiz);
        vP += vsiz;
        memcpy(vP, FIELDVALSEPSTR, FIELDVALSEPSTRSIZE);
        vP += FIELDVALSEPSTRSIZE;
        memcpy(vP, kbuf+1, vFieldName-kbuf-2);
        vP += vFieldName-kbuf-2;
//        strcpy(vIdxName, IDXKEY);
//        strncat(vIdxName, vFieldName, vFieldNameSize);
//        strcat(vIdxName, KEYSEPSTR);
//        strncat(vIdxName, vbuf, vsiz);
//        
//        strcat(vIdxName, FIELDVALSEPSTR);
//        strncat(vIdxName, kbuf+1, vFieldName-kbuf-2);
        vResult = tcbdbout(aDB->IndexDB, vIdxName, vP-vIdxName);
        if (!vResult) {
          tcbdbsetecode(aDB->DB, TCEINVALID, __FILE__, __LINE__, __func__);
#if defined(DEBUG)
          vIdxName[vP-vIdxName] = '\0';
          fprintf(stderr, "field(%s) delete index failed: \n", vIdxName);
#endif
        }else {
          vP = vIdxName;
          memcpy(vP, IDXCOUNTCONFIGKEY, strlen(IDXCOUNTCONFIGKEY));
          vP += strlen(IDXCOUNTCONFIGKEY);
          memcpy(vP, vFieldName, vFieldNameSize);
          vP += vFieldNameSize;
          tcHDBAddUInt64(aDB->ConfigDB, vIdxName, vP-vIdxName, -1);
        }

      } // if (vIsIndex >=0)
      else {
        vResult = true;
      }

    } 
  }
  return vResult;
}

static inline bool tcDBDeleteIndexImpl(TBTreeDatabase *aDB, const char *kbuf, int ksiz) {
  bool vResult = true;
  char vIdxName[MAXKEYLEN+1];
  //int vIsIndex;
  if (kbuf[0] == FIELDSEPCHAR) { //a single field storage.
    vResult = tcDBDeleteFieldIndexImpl(aDB, kbuf, ksiz);
  }
  else {
    strcpy(vIdxName, FIELDSEPSTR);
    strncat(vIdxName, kbuf, ksiz);
    strcat(vIdxName, FIELDSEPSTR);
    int vSize = strlen(FIELDSEPSTR) + strlen(FIELDSEPSTR) + ksiz;
    TCLIST *vList = tcbdbfwmkeys2(aDB->DB, vIdxName, -1);
    int vCount = -1;
    if (vList) vCount = tclistnum(vList);
    for (int i = 0; i < vCount; i++) {
      const char *vKBuf;
      int vKSiz;
      TCLISTVAL(vKBuf, vList, i, vKSiz); //get a field
      strncat(vIdxName, vKBuf, vKSiz);
      if (!tcDBDeleteFieldIndexImpl(aDB, vIdxName, vSize + vKSiz)) vResult = false;
      vIdxName[vSize] = '\0';
    } //for loop fields
  }
  
  return vResult;
  
}

static bool tcDBDeleteImpl(TBTreeDatabase *aDB, const char *kbuf, int ksiz, const TCBDBOUTPROC aOutProc){
  bool vResult = true;

  char vIdxName[MAXKEYLEN+1];
  //int vIsIndex;
  if (kbuf[0] == FIELDSEPCHAR) { //a single field storage.
    tcDBDeleteFieldIndexImpl(aDB, kbuf, ksiz);
    vResult = aOutProc(aDB->DB, kbuf, ksiz);
    if (vResult) { //update columns info.
      const char *vFieldName = strrchr(kbuf, FIELDSEPCHAR);
      //printf("vFieldName:%s, %d\n", vFieldName, vFieldName-kbuf-2 );
      if (vFieldName && (vFieldName-kbuf-2 > 0)) { //have fieldName
        vFieldName++;
        strcpy(vIdxName, KEYSEPSTR);
        strncat(vIdxName, kbuf+1, vFieldName-kbuf-2);
        int vSColumnsSize;
        const char *vSColumns = tcbdbget(aDB->DB, vIdxName, vFieldName-kbuf-2+strlen(KEYSEPSTR), &vSColumnsSize);
        if (vSColumns) {
          TCLIST *vColumns = tcstrsplit2(vSColumns, vSColumnsSize);
          int i = tclistlsearch(vColumns, vFieldName, ksiz - (vFieldName-kbuf));
          //printf("searched col:%d\n", i);
          if (i >= 0) {
            TCFREE(tclistremove2(vColumns, i));
            //tcfree(vTemp);
            TCXSTR *vS = tcxstrnew3(512);
            for (i=0; i < tclistnum(vColumns); i++) {
              const char *vFieldKey;
              int vFieldKeySize;
              TCLISTVAL(vFieldKey, vColumns, i, vFieldKeySize);
              TCXSTRCAT(vS, vFieldKey, vFieldKeySize+1);
            } //for
            if (TCXSTRSIZE(vS) > 0) TCXSTRSIZE(vS)=TCXSTRSIZE(vS)-1;
//            if (TCXSTRSIZE(vS) > 0) {
              //printf("%d. vs:%s\n", TCXSTRSIZE(vS), TCXSTRPTR(vS));
              tcbdbput(aDB->DB, vIdxName, vFieldName-kbuf-2+strlen(KEYSEPSTR), TCXSTRPTR(vS), TCXSTRSIZE(vS));
//            }
//            else {
//              tcbdbput(aDB->DB, vIdxName, vFieldName-kbuf-2+strlen(KEYSEPSTR), "\0", 0);
//            }

            tcxstrdel(vS);
          }
          
          tclistdel(vColumns);
        } //if vSColumns != NULL
      } //if have fieldName
    } //if (vResult)
  }
  else {
    strcpy(vIdxName, FIELDSEPSTR);
    strncat(vIdxName, kbuf, ksiz);
    strcat(vIdxName, FIELDSEPSTR);
    int vSize = strlen(FIELDSEPSTR) + strlen(FIELDSEPSTR) + ksiz;
    vResult = tcDBDeleteMatchedKeysImpl(aDB, vIdxName, vSize);
/*
    TCLIST *vList = tcbdbfwmkeys(aDB->DB, vIdxName, vSize, -1);
    for (int i = 0; i < tclistnum(vList); i++) {
      const char *vKBuf;
      int vKSiz;
      TCLISTVAL(vKBuf, vList, i, vKSiz); //get a field
      //strncat(vIdxName, vKBuf, vKSiz);
      tcDBDeleteFieldIndexImpl(aDB, vKBuf, vKSiz);
      if (!aOutProc(aDB->DB, vKBuf, vKSiz)) vResult = false;
      //vIdxName[vSize] = '\0';
    } //for loop fields
    tclistdel(vList);
 */
    if (vResult) {
      strcpy(vIdxName, KEYSEPSTR);
      strncat(vIdxName, kbuf, ksiz);
      vResult = aOutProc(aDB->DB, vIdxName, ksiz+strlen(KEYSEPSTR));
      if (vResult) {
        // dec record count
        tcHDBAddUInt64(aDB->ConfigDB, REC_COUNTCFGKEY, strlen(REC_COUNTCFGKEY), -1);
      }
    }
  }
  //printf("result:%d\n", vResult);
  
  return vResult;
}

//return TDBITVOID means not an index. 
static inline int tcDBGetFieldIndexType(TBTreeDatabase *aDB, const char *aFieldName, int aFieldNameSize) {
  char vIdxName[MAXKEYLEN+1];
  char *vP = vIdxName;
  memcpy(vP, IDXCONFIGKEY, strlen(IDXCONFIGKEY));
  vP+=strlen(IDXCONFIGKEY);
  memcpy(vP, aFieldName, aFieldNameSize); //add null char with end.
  vP+=aFieldNameSize;
  //strcpy(vIdxName, IDXCONFIGKEY);
  //strncat(vIdxName, aFieldName, aFieldNameSize);
  int vResult = 0;
  if (tchdbget3(aDB->ConfigDB, vIdxName, vP-vIdxName, &vResult, sizeof(vResult)) > 0) {
    vResult--;
  }
  else {
    vResult = TDBITVOID; //not an index. 
  }

  return vResult;
}

static inline bool tcDBIsFieldIndex(TBTreeDatabase *aDB, const char *aFieldName, int aFieldNameSize) {
  char vIdxName[MAXKEYLEN+1];
  char *vP = vIdxName;
  memcpy(vP, IDXCONFIGKEY, strlen(IDXCONFIGKEY));
  vP+=strlen(IDXCONFIGKEY);
  memcpy(vP, aFieldName, aFieldNameSize); //add null char with end.
  vP+=aFieldNameSize;
  //strcpy(vIdxName, IDXCONFIGKEY);
  //strncat(vIdxName, aFieldName, aFieldNameSize);
  return tchdbvsiz(aDB->ConfigDB, vIdxName, vP-vIdxName) >= 0;
}
/*
   put ".[KeyName].[FieldName]"  "Value"
   if [FieldName] is Index(_DB/index/_conf/[FieldName]) then put the index: /[FieldName]/[FieldValue]/[KeyName]
 */
static inline bool tcDBPutFieldIndexImpl(TBTreeDatabase *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz) {
  bool vResult = true;
  //a single field storage.
  char vIdxName[MAXKEYLEN+1];
  //int vIsIndex;
  const char *vFieldName = strrchr(kbuf, FIELDSEPCHAR);
  if ((vFieldName) && (vFieldName-kbuf-2 > 0)) {
    vFieldName++;
    int vFieldNameSize = ksiz - (vFieldName-kbuf);
    if ((strlen(IDXCONFIGKEY) + vFieldNameSize < MAXKEYLEN) && (strlen(IDXKEY) + ksiz + vsiz < MAXKEYLEN)) {
      //strcpy(vIdxName, IDXCONFIGKEY);
      //strcat(vIdxName, vFieldName);
      //vIsIndex = tchdbvsiz2(aDB->ConfigDB, vIdxName);
      if (tcDBIsFieldIndex(aDB, vFieldName, vFieldNameSize)) { //it is the index field, need to store the index.
        //strncpy(vIdxName, IDXKEY, strlen(IDXKEY)+1);
        char *vP = vIdxName;
        memcpy(vP, IDXKEY, strlen(IDXKEY));
        vP += strlen(IDXKEY);
        memcpy(vP, vFieldName, vFieldNameSize);
        vP += vFieldNameSize;
        memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR));
        vP += strlen(KEYSEPSTR);
        memcpy(vP, vbuf, vsiz);
        vP += vsiz;
        memcpy(vP, FIELDVALSEPSTR, FIELDVALSEPSTRSIZE);
        vP += FIELDVALSEPSTRSIZE;
        memcpy(vP, kbuf+1, vFieldName-kbuf-2);
        vP += vFieldName-kbuf-2;
        int vIdxNameSize = vP - vIdxName;
        if (tcbdbvsiz(aDB->IndexDB, vIdxName, vIdxNameSize) < 0) { //a new index 
          char vIdxCountCfg[MAXKEYLEN+1] = IDXCOUNTCONFIGKEY;
          vP = vIdxCountCfg;
          vP += strlen(IDXCOUNTCONFIGKEY);
          memcpy(vP, vFieldName, vFieldNameSize);
          vP += vFieldNameSize;
          tcHDBAddUInt64(aDB->ConfigDB, vIdxCountCfg, vP-vIdxCountCfg, 1);
        }
        if (!tcbdbput(aDB->IndexDB, vIdxName, vIdxNameSize, kbuf+1, vFieldName-kbuf-2)) {
          vResult = false;
          tcbdbsetecode(aDB->DB, TCEINVALID, __FILE__, __LINE__, __func__);
#if defined(DEBUG)
          fprintf(stderr, "field(%s) store index failed: \n", vFieldName);
#endif
        }
      } // if (vIsIndex >=0)
    }
    else {
      tcbdbsetecode(aDB->DB, TCEINVALID, __FILE__, __LINE__, __func__);
#if defined(DEBUG)
      fprintf(stderr, "the field too long(%s) to index it \n", vFieldName);
#endif
    }//if-check index whether exceed the Max index Key Length
  }
  return vResult;
}

static bool tcDBPutColumnsImpl(TBTreeDatabase *aDB, const char *aKey, int aKeySize, TCMAP *aColumns, int aDMode) {
  assert(aDB && aKey && aKeySize >= 0 && aColumns);
  bool vResult = true;
  char vMainKeyBuf[MAXKEYLEN+1];
  strcpy(vMainKeyBuf, KEYSEPSTR);
  strncat(vMainKeyBuf, aKey, aKeySize);
  int vColumnInfoSize;
  char *vColumnInfoStr = tcbdbget(aDB->DB, vMainKeyBuf, aKeySize+strlen(KEYSEPSTR), &vColumnInfoSize);
  if(vColumnInfoStr){ //update exists record?
    if(aDMode == TDBPDKEEP){
      tcDBRaiseError(aDB, TCEKEEP, __FILE__, __LINE__, __func__);
      TCFREE(vColumnInfoStr);
      return false;
    }
    TCLIST *vColumnInfo = tcstrsplit2(vColumnInfoStr, vColumnInfoSize);
    TCFREE(vColumnInfoStr);
    tcmapiterinit(aColumns);
    TCXSTR *vSingleFieldKey = tcxstrnew3(512);
    int vFieldNameSize;
    const char *vFieldName;
    while(vResult && ((vFieldName = tcmapiternext(aColumns, &vFieldNameSize)) != NULL)){
      int vFieldValueSize;
      const char *vFieldValue;
      int i = tclistlsearch(vColumnInfo, vFieldName, vFieldNameSize);
      if (i >= 0) { //update field
        vFieldValue = tcmapiterval(vFieldName, &vFieldValueSize);
        tcxstrcatKeyField(vSingleFieldKey, aKey, aKeySize, vFieldName);
        vResult = tcbdbput(aDB->DB, TCXSTRPTR(vSingleFieldKey), TCXSTRSIZE(vSingleFieldKey), vFieldValue, vFieldValueSize);
        if (vResult) {
          if (!tcDBPutFieldIndexImpl(aDB, TCXSTRPTR(vSingleFieldKey), TCXSTRSIZE(vSingleFieldKey), vFieldValue, vFieldValueSize)) 
            vResult = false;
        }
      } //end update field
      else { //this is a new field
        if (aDMode == TDBPDCAT) {
          vFieldValue = tcmapiterval(vFieldName, &vFieldValueSize);
          tclistpush(vColumnInfo, vFieldName, vFieldNameSize);
          tcxstrcatKeyField(vSingleFieldKey, aKey, aKeySize, vFieldName);
          vResult = tcbdbput(aDB->DB, TCXSTRPTR(vSingleFieldKey), TCXSTRSIZE(vSingleFieldKey), vFieldValue, vFieldValueSize);
          if (vResult) {
            if (!tcDBPutFieldIndexImpl(aDB, TCXSTRPTR(vSingleFieldKey), TCXSTRSIZE(vSingleFieldKey), vFieldValue, vFieldValueSize))
              vResult = false;
          }
        }
        else {
          tcDBRaiseError(aDB, TCEKEEP, __FILE__, __LINE__, "Do not allow to add a new field when update exists record, use the putcat instead.");
          vResult = false;
        }
      } //end new field
    } //while loop for all fields
    if (vResult) {
      tcxstrclear(vSingleFieldKey);
      TCXSTRCAT(vSingleFieldKey, KEYSEPSTR, strlen(KEYSEPSTR));
      TCXSTRCAT(vSingleFieldKey, aKey, aKeySize);
      vColumnInfoStr = tcstrjoin2(vColumnInfo, &vColumnInfoSize);
      //store the field(column) name infos for this row.
      vResult =  tcbdbput(aDB->DB, TCXSTRPTR(vSingleFieldKey), TCXSTRSIZE(vSingleFieldKey), vColumnInfoStr, vColumnInfoSize);
      TCFREE(vColumnInfoStr);
      //}
    }
    tcxstrdel(vSingleFieldKey);
    tclistdel(vColumnInfo);
  } //if(vColumnInfo)
  else { //this is a new record(row)
    tcmapiterinit(aColumns);
    TCXSTR *vSingleFieldKey = tcxstrnew3(512);
    TCXSTR *vSColumns = tcxstrnew3(512);
    int vFieldNameSize;
    const char *vFieldName;
    while(vResult && ((vFieldName = tcmapiternext(aColumns, &vFieldNameSize)) != NULL)){
      int vFieldValueSize;
      const char *vFieldValue;
      vFieldValue = tcmapiterval(vFieldName, &vFieldValueSize);
      TCXSTRCAT(vSColumns, vFieldName, vFieldNameSize+1);

      tcxstrcatKeyField(vSingleFieldKey, aKey, aKeySize, vFieldName);
      //printf("append field %s=%s\n", TCXSTRPTR(vSingleFieldKey), vFieldValue);
      vResult = tcbdbput(aDB->DB, TCXSTRPTR(vSingleFieldKey), TCXSTRSIZE(vSingleFieldKey), vFieldValue, vFieldValueSize);
      if (vResult) {
        //printf("put ok\n");
        if (!tcDBPutFieldIndexImpl(aDB, TCXSTRPTR(vSingleFieldKey), TCXSTRSIZE(vSingleFieldKey), vFieldValue, vFieldValueSize))
          vResult = false;
        //if (!vResult) printf("put index faild.\n");
      }
    } //while loop for all fields
    if (vResult && TCXSTRSIZE(vSColumns) > 0) {
      tcxstrclear(vSingleFieldKey);
      TCXSTRCAT(vSingleFieldKey, KEYSEPSTR, strlen(KEYSEPSTR));
      TCXSTRCAT(vSingleFieldKey, aKey, aKeySize);
      //store the field(column) name infos for this row.
      vResult = tcbdbput(aDB->DB, TCXSTRPTR(vSingleFieldKey), TCXSTRSIZE(vSingleFieldKey), TCXSTRPTR(vSColumns), TCXSTRSIZE(vSColumns)-1);
      // inc record count
      tcHDBAddUInt64(aDB->ConfigDB, REC_COUNTCFGKEY, strlen(REC_COUNTCFGKEY), 1);
      //}
    }
    tcxstrdel(vSingleFieldKey);
    tcxstrdel(vSColumns);
  }
  return vResult;
}

/*
 Put multi-fields with a Key:
   put [KeyName] [FieldName]\0[FieldValue]\0[FieldName]\0[FieldValue]
 Put a single field with a Key:
   put .[keyName].[FieldName] value
 */
static bool tcDBReplaceImpl(TBTreeDatabase *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int aDMode){
  bool vResult = false;
  if (isKeepReservedChar(kbuf)) {
#if defined(DEBUG)
    fprintf(stderr, "Put error: the key is keep reserved(%s)\n", kbuf);
#endif
    tcbdbsetecode(aDB->DB, TCEINVALID, __FILE__, __LINE__, __func__);
    return vResult;
  }
  
//  char vIdxName[MAXKEYLEN+1];
//  int vIsIndex;
  if (kbuf[0] == FIELDSEPCHAR) { //a single field storage.
    TCBDBPUTPROC aPutProc;
    switch (aDMode) {
      case TDBPDCAT:
        aPutProc = tcbdbputcat;
        break;
      case TDBPDKEEP:
        aPutProc = tcbdbputkeep;
        break;
      case TDBPDDUP:
        aPutProc = tcbdbputdup;
        break;
      case TDBPDDUPB:
        aPutProc = tcbdbputdupback;
        break;
      default:
        aPutProc = tcbdbput;
        break;
    }
    vResult = aPutProc(aDB->DB, kbuf, ksiz, vbuf, vsiz);
    if (vResult) {
      tcDBPutFieldIndexImpl(aDB, kbuf, ksiz, vbuf, vsiz);
    }
  }
  else {
    TCMAP *aCols = tcstrsplit4(vbuf, vsiz);
    if (tcmaprnum(aCols) > 0) {
      vResult = tcDBPutColumnsImpl(aDB, kbuf, ksiz, aCols, aDMode);
    }
    else {
      TCBDBPUTPROC aPutProc;
      switch (aDMode) {
        case TDBPDCAT:
          aPutProc = tcbdbputcat;
          break;
        case TDBPDKEEP:
          aPutProc = tcbdbputkeep;
          break;
        case TDBPDDUP:
          aPutProc = tcbdbputdup;
          break;
        case TDBPDDUPB:
          aPutProc = tcbdbputdupback;
          break;
        default:
          aPutProc = tcbdbput;
          break;
      }
      vResult = aPutProc(aDB->DB, kbuf, ksiz, vbuf, vsiz);
    }
    tcmapdel(aCols);
/*
    TCLIST *aCols = tcstrsplit2(vbuf, vsiz);
    int vColumnCount = tclistnum(aCols);
#if defined(DEBUG)
    //%llu means uint64_t
    //fprintf(stderr, "Put ksiz:%d fieldCount:%d\n",ksiz, vColumnCount);
#endif
    if ((vColumnCount > 0)&& (vColumnCount % 2 == 0)) { //
      //tcmapiterinit(aCols);
      const char *vFieldKey;
      int vFieldKeySize; 
      const char *vFieldValue;
      TCXSTR *vS = tcxstrnew3(512);
      TCXSTR *vSColumns = tcxstrnew3(512);
      for (int i=0; i < vColumnCount; i+=2) {
      //while((vFieldKey = tcmapiternext(aCols, &vFieldKeySize)) != NULL) {
        //printf("(%d)", i);
        int vFieldSize;
        TCLISTVAL(vFieldKey, aCols, i, vFieldKeySize);
        TCLISTVAL(vFieldValue, aCols, i+1, vFieldSize);
        //printf("%s=%s\n", vFieldKey, vFieldValue);
        //vFieldKey = tclistval(aCols, i, &vFieldKeySize);
        //vFieldValue = tclistval(aCols, i+1, &vFieldSize);
        //printf("field:%s,value:%s\n", vFieldKey, vFieldValue);
        TCXSTRCAT(vSColumns, vFieldKey, vFieldKeySize+1);
        //printf("%s\n", TCXSTRPTR(vSColumns));
        
        tcxstrcatKeyField(vS, kbuf, ksiz, vFieldKey); 
        //printf("vFieldValue:%s, vS:%s\n", vFieldValue, TCXSTRPTR(vS));
        vResult = aPutProc(aDB->DB, TCXSTRPTR(vS), TCXSTRSIZE(vS), vFieldValue, vFieldSize);
        if (vResult) {
          tcDBPutFieldIndexImpl(aDB, TCXSTRPTR(vS), TCXSTRSIZE(vS), vFieldValue, vFieldSize);
        }
      } //while throughout the columns
      if (vResult) {
//        char numbuf[TCNUMBUFSIZ];
//        sprintf(numbuf, "%d", tcmaprnum(aCols));
        tcxstrclear(vS);
        TCXSTRCAT(vS, KEYSEPSTR, strlen(KEYSEPSTR));
        TCXSTRCAT(vS, kbuf, ksiz);
        //if (tcbdbvsiz(aDB->DB, TCXSTRPTR(vS), TCXSTRSIZE(vS)) < 0) {
          //vResult =  tcbdbput(aDB->DB, TCXSTRPTR(vS), TCXSTRSIZE(vS), "\0", 1);//tcbdbaddint(aDB->DB, TCXSTRPTR(vS), TCXSTRSIZE(vS), tcmaprnum(aCols));
          //store the field(column) names of this row.
        vResult =  aPutProc(aDB->DB, TCXSTRPTR(vS), TCXSTRSIZE(vS), TCXSTRPTR(vSColumns), TCXSTRSIZE(vSColumns)-1);
        //}
      }
      tcxstrdel(vS);
      tcxstrdel(vSColumns);
    } //if (tclistnum(aCols) > 0)
    else {
      vResult = aPutProc(aDB->DB, kbuf, ksiz, vbuf, vsiz);
    }
    //tcmapdel(aCols);
    tclistdel(aCols);
 */
  }
  //printf("return:%d\n",vResult);
  return vResult;
}

/*
bool tcDBReplaceProc(TBTreeDatabase *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz, const TCBDBPUTPROC aPutProc){
  return tcDBReplaceImpl(aDB, kbuf, ksiz, vbuf, vsiz, aPutProc);  
}
*/
/* Store a record into a database object.
 `aDB' specifies the database object.
 `kbuf' specifies the pointer to the region of the key.
 `ksiz' specifies the size of the region of the key.
 `vbuf' specifies the pointer to the region of the value.
 `vsiz' specifies the size of the region of the value.
 If successful, the return value is true, else, it is false. */
bool tcDBReplace(void *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz){
  assert(aDB && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
  if(!DBLOCKMETHOD(aDB, true)) return false;
  if(!((TBTreeDatabase*)aDB)->Opened || !((TBTreeDatabase*)aDB)->Writable){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  bool vResult;
  
  vResult = tcDBReplaceImpl(((TBTreeDatabase *)aDB), (void *)kbuf, ksiz, (void *)vbuf, vsiz, TDBPDOVER);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}


/* Store a new record into a database object.
 `aDB' specifies the database object.
 `kbuf' specifies the pointer to the region of the key.
 `ksiz' specifies the size of the region of the key.
 `vbuf' specifies the pointer to the region of the value.
 `vsiz' specifies the size of the region of the value.
 If successful, the return value is true, else, it is false. */
bool tcDBInsert(void *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz){
  assert(aDB && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
  if(!DBLOCKMETHOD(aDB, true)) return false;
  if(!((TBTreeDatabase*)aDB)->Opened || !((TBTreeDatabase*)aDB)->Writable){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  bool vResult;
  
//  if (vsiz > 4 && *vbuf == '{' && *(vbuf+vsiz-1) == '}') {
//    vResult = tcDBReplaceProc(((TBTreeDatabase *)aDB), kbuf, ksiz, vbuf, vsiz, tcbdbputkeep);
//  }
//  else {
//    vResult = tcbdbputkeep(((TBTreeDatabase *)aDB)->DB, kbuf, ksiz, vbuf, vsiz);
//  }
  vResult = tcDBReplaceImpl(((TBTreeDatabase *)aDB), kbuf, ksiz, vbuf, vsiz, TDBPDKEEP);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

/* Remove a record of a multiple database object.
 `aDB' specifies the multiple database object.
 `kbuf' specifies the pointer to the region of the key.
 `ksiz' specifies the size of the region of the key.
 If successful, the return value is true, else, it is false. */
bool tcDBDelete(void *aDB, const char *kbuf, int ksiz){
  assert(aDB && kbuf && ksiz >= 0);
  if(!DBLOCKMETHOD(aDB, true)) return false;
  if(!((TBTreeDatabase*)aDB)->Opened || !((TBTreeDatabase*)aDB)->Writable){
    tcDBRaiseError(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(aDB);
    return false;
  }
  bool vResult = tcDBDeleteImpl(aDB, kbuf, ksiz, tcbdbout);
  DBUNLOCKMETHOD(aDB);
  return vResult;
}

void tcDBRaiseError(void *aDB, int aErrCode, const char *aFilename, int aLine, const char *aFunc){
  assert(aDB && aFilename && aLine >= 1 && aFunc);
  tchdbsetecode(((TBTreeDatabase *) aDB)->DB->hdb, aErrCode, aFilename, aLine, aFunc);
}

/* Lock a method of the B+ tree database object.
 `db' specifies the B+ tree database object.
 `wr' specifies whether the lock is writer or not.
 If successful, the return value is true, else, it is false. */
static bool tcDBLockMethod(TBTreeDatabase *db, bool wr){
  assert(db);
  if(wr ? pthread_rwlock_wrlock(db->mmtx) != 0 : pthread_rwlock_rdlock(db->mmtx) != 0){
    tcDBRaiseError(db, TCETHREAD, __FILE__, __LINE__, __func__);
    return false;
  }
  TCTESTYIELD();
  return true;
}


/* Unlock a method of the B+ database object.
 `db' specifies the database object.
 If successful, the return value is true, else, it is false. */
static bool tcDBUnLockMethod(TBTreeDatabase *db){
  assert(db);
  assert(db->mmtx);
  if(pthread_rwlock_unlock(db->mmtx) != 0){
    tcDBRaiseError(db, TCETHREAD, __FILE__, __LINE__, __func__);
    return false;
  }
  TCTESTYIELD();
  return true;
}


/* Perform dynamic defragmentation of a B+ tree database object.
 `aDB' specifies the B+ tree database object connected as a writer.
 `aStep' specifie the number of steps.  If it is not more than 0, the whole file is defragmented
 gradually without keeping a continuous lock.
 If successful, the return value is true, else, it is false. */
bool tcDBDefrag(TBTreeDatabase *aDB, int64_t aStep) {
  assert(aDB);
  bool vResult = tcbdbdefrag(aDB->DB, aStep);
  if (!tcbdbdefrag(aDB->IndexDB, aStep)) {
    vResult = false;
  }
  return vResult;      
}

bool tcDBCacheClear(TBTreeDatabase *aDB) {
  assert(aDB);
  bool vResult = tcbdbcacheclear(aDB->DB);
  if (!tcbdbcacheclear(aDB->IndexDB)) vResult = false;
  if (!tchdbcacheclear(aDB->ConfigDB)) vResult = false;
  return vResult;
}

/* -----------Query --------------*/
static long double tcDBatof(const char *str);

/* Compare two primary keys by number ascending.
 `a' specifies a key.
 `b' specifies of the other key.
 The return value is positive if the former is big, negative if the latter is big, 0 if both
 are equivalent. */
static int tcDBCmpPkeyNumAsc(const TCLISTDATUM *a, const TCLISTDATUM *b){
  assert(a && b);
  return tccmpdecimal(a->ptr, a->size, b->ptr, b->size, NULL);
}


/* Compare two primary keys by number descending.
 `a' specifies a key.
 `b' specifies of the other key.
 The return value is positive if the former is big, negative if the latter is big, 0 if both
 are equivalent. */
static int tcDBCmpPkeyNumDesc(const TCLISTDATUM *a, const TCLISTDATUM *b){
  assert(a && b);
  return tccmpdecimal(b->ptr, b->size, a->ptr, a->size, NULL);
}

/* Compare two sort records by string ascending.
 `a' specifies a key.
 `b' specifies of the other key.
 The return value is positive if the former is big, negative if the latter is big, 0 if both
 are equivalent. */
static int tcDBCmpSortRecStrAsc(const TDBSORTREC *a, const TDBSORTREC *b){
  assert(a && b);
  if(!a->vbuf){
    if(!b->vbuf) return 0;
    return 1;
  }
  if(!b->vbuf){
    if(!a->vbuf) return 0;
    return -1;
  }
  int rv;
  TCCMPLEXICAL(rv, a->vbuf, a->vsiz, b->vbuf, b->vsiz);
  return rv;
}


/* Compare two sort records by string descending.
 `a' specifies a key.
 `b' specifies of the other key.
 The return value is positive if the former is big, negative if the latter is big, 0 if both
 are equivalent. */
static int tcDBCmpSortRecStrDesc(const TDBSORTREC *a, const TDBSORTREC *b){
  assert(a && b);
  if(!a->vbuf){
    if(!b->vbuf) return 0;
    return 1;
  }
  if(!b->vbuf){
    if(!a->vbuf) return 0;
    return -1;
  }
  int rv;
  TCCMPLEXICAL(rv, a->vbuf, a->vsiz, b->vbuf, b->vsiz);
  return -rv;
}


/* Compare two sort records by number ascending.
 `a' specifies a key.
 `b' specifies of the other key.
 The return value is positive if the former is big, negative if the latter is big, 0 if both
 are equivalent. */
static int tcDBCmpSortRecNumAsc(const TDBSORTREC *a, const TDBSORTREC *b){
  assert(a && b);
  if(!a->vbuf){
    if(!b->vbuf) return 0;
    return 1;
  }
  if(!b->vbuf){
    if(!a->vbuf) return 0;
    return -1;
  }
  long double anum = tcDBatof(a->vbuf);
  long double bnum = tcDBatof(b->vbuf);
  if(anum < bnum) return -1;
  if(anum > bnum) return 1;
  return 0;
}


/* Compare two sort records by number descending.
 `a' specifies a key.
 `b' specifies of the other key.
 The return value is positive if the former is big, negative if the latter is big, 0 if both
 are equivalent. */
static int tcDBCmpSortRecNumDesc(const TDBSORTREC *a, const TDBSORTREC *b){
  assert(a && b);
  if(!a->vbuf){
    if(!b->vbuf) return 0;
    return 1;
  }
  if(!b->vbuf){
    if(!a->vbuf) return 0;
    return -1;
  }
  long double anum = tcDBatof(a->vbuf);
  long double bnum = tcDBatof(b->vbuf);
  if(anum < bnum) return 1;
  if(anum > bnum) return -1;
  return 0;
}

/* Get the count of corresponding records of a query object.
 `qry' specifies the query object.
 The return value is the count of corresponding records. */
int tcDBQueryCount(TDBQuery *qry){
  assert(qry);
  return qry->count;
}

/* Create a query object. */
TDBQuery *tcDBQueryCreate(void *aDB){
  assert(aDB);
  TDBQuery *qry;
  TCMALLOC(qry, sizeof(*qry));
  qry->DB = (TBTreeDatabase *)aDB;
  TCMALLOC(qry->Conditions, sizeof(qry->Conditions[0]) * 2);
  qry->ConditionCount = 0;
  qry->OrderColumnName = NULL;
  qry->OrderType = TDBQOSTRASC;
  qry->max = INT_MAX;
  qry->skip = 0;
  qry->hint = tcxstrnew3(TDBHINTUSIZ);
  qry->count = 0;
  return qry;
}

/* Delete a query object. */
void tcDBQueryFree(TDBQuery *qry){
  assert(qry);
  tcxstrdel(qry->hint);
  TCFREE(qry->OrderColumnName);
  TDBQueryCondition *conds = qry->Conditions;
  int vConditionCount = qry->ConditionCount;
  for(int i = 0; i < vConditionCount; i++){
    TDBQueryCondition *cond = conds + i;
    if(cond->regex){
      regfree(cond->regex);
      TCFREE(cond->regex);
    }
    TCFREE(cond->expr);
    TCFREE(cond->ColumnName);
  }
  TCFREE(conds);
  TCFREE(qry);
}

/* Add a narrowing condition to a query object. */
void tcDBQueryAddCondition(TDBQuery *qry, const char *name, int op, const char *expr){
  assert(qry && name && expr);
  int vConditionCount = qry->ConditionCount;
  TCREALLOC(qry->Conditions, qry->Conditions, sizeof(qry->Conditions[0]) * (vConditionCount + 1));
  TDBQueryCondition *cond = qry->Conditions + vConditionCount;
  int nsiz = strlen(name);
  int esiz = strlen(expr);
  TCMEMDUP(cond->ColumnName, name, nsiz);
  cond->ColumnNameSize = nsiz;
  bool sign = true;
  if(op & TDBQCNEGATE){
    op &= ~TDBQCNEGATE;
    sign = false;
  }
  bool noidx = false;
  if(op & TDBQCNOIDX){
    op &= ~TDBQCNOIDX;
    noidx = true;
  }
  cond->op = op;
  cond->sign = sign;
  cond->noidx = noidx;
  TCMEMDUP(cond->expr, expr, esiz);
  cond->esiz = esiz;
  cond->regex = NULL;
  if(op == TDBQCSTRRX){
    const char *rxstr = expr;
    int rxopt = REG_EXTENDED | REG_NOSUB;
    if(*rxstr == '*'){
      rxopt |= REG_ICASE;
      rxstr++;
    }
    regex_t rxbuf;
    if(regcomp(&rxbuf, rxstr, rxopt) == 0){
      TCMALLOC(cond->regex, sizeof(rxbuf));
      memcpy(cond->regex, &rxbuf, sizeof(rxbuf));
    }
  }
  qry->ConditionCount++;
}


/* Set the order of a query object. */
void tcDBQuerySetOrder(TDBQuery *qry, const char *name, int type){
  assert(qry && name);
  if(qry->OrderColumnName) TCFREE(qry->OrderColumnName);
  qry->OrderColumnName = tcstrdup(name);
  qry->OrderType = type;
}


/* Set the limit number of records of the result of a query object. */
void tcDBQuerySetLimit(TDBQuery *qry, int max, int skip){
  assert(qry);
  qry->max = (max >= 0) ? max : INT_MAX;
  qry->skip = (skip > 0) ? skip : 0;
}

/* Convert a string to a real number.
 `str' specifies the string.
 The return value is the real number. */
static long double tcDBatof(const char *str){
  assert(str);
  while(*str > '\0' && *str <= ' '){
    str++;
  }
  int sign = 1;
  if(*str == '-'){
    str++;
    sign = -1;
  } else if(*str == '+'){
    str++;
  }
  if(tcstrifwm(str, "inf")) return HUGE_VALL * sign;
  if(tcstrifwm(str, "nan")) return nanl("");
  long double num = 0;
  int col = 0;
  while(*str != '\0'){
    if(*str < '0' || *str > '9') break;
    num = num * 10 + *str - '0';
    str++;
    if(num > 0) col++;
  }
  if(*str == '.'){
    str++;
    long double fract = 0.0;
    long double base = 10;
    while(col < TDBNUMCOLMAX && *str != '\0'){
      if(*str < '0' || *str > '9') break;
      fract += (*str - '0') / base;
      str++;
      col++;
      base *= 10;
    }
    num += fract;
  }
  return num * sign;
}

/* Convert a value to a real number.
 `vbuf' specifies the value, it maybe a int or double or string.
 The return value is the real number. */
static long double tcDBValueToFloat(const void *vbuf, int vsiz){
  return tcDBatof(vbuf);
/*
   switch (vsiz) {
    case sizeof(int):
      return *(int*) vbuf;
    case sizeof(double):
      return *(double*) vbuf;
    default:
      return tcDBatof(vbuf);
  }
 */
}

/* Check whether a decimal is between two tokens in another string.
 `vbuf' specifies the target value.
 `expr' specifies the operation value.
 If they matches, the return value is true, else it is false. */
static bool tcDBCheckValueBetween(const double value, const char *expr){
  assert(expr);
  while(*expr == ' ' || *expr == ','){
    expr++;
  }
  const char *pv = expr;
  while(*pv != '\0' && *pv != ' ' && *pv != ','){
    pv++;
  }
  if(*pv != ' ' && *pv != ',') pv = " ";
  pv++;
  while(*pv == ' ' || *pv == ','){
    pv++;
  }
  long double lower = tcDBatof(expr);
  long double upper = tcDBatof(pv);
  if(lower > upper){
    long double swap = lower;
    lower = upper;
    upper = swap;
  }
  return value >= lower && value <= upper;
}

/* Check whether a number is equal to at least one token in another string.
 `vbuf' specifies the target value.
 `expr' specifies the operation value.
 If they matches, the return value is true, else it is false. */
static bool tcDBCheckValueEquAtLeastOnce(const double value, const char *expr){
  assert(expr);
  const char *sp = expr;
  while(*sp != '\0'){
    while(*sp == ' ' || *sp == ','){
      sp++;
    }
    const char *ep = sp;
    while(*ep != '\0' && *ep != ' ' && *ep != ','){
      ep++;
    }
    if(ep > sp && value == tcDBatof(sp)) return true;
    sp = ep;
  }
  return false;
}

/* Check whether a string includes all tokens in another string.
 `vbuf' specifies the column value.
 `expr' specifies the operand expression.
 If they matches, the return value is true, else it is false. */
static bool tcDBCheckStrAnd(const char *vbuf, const char *expr){
  assert(vbuf && expr);
  const unsigned char *sp = (unsigned char *)expr;
  while(*sp != '\0'){
    while((*sp != '\0' && *sp <= ' ') || *sp == ','){
      sp++;
    }
    const unsigned char *ep = sp;
    while(*ep > ' ' && *ep != ','){
      ep++;
    }
    if(ep > sp){
      bool hit = false;
      const unsigned char *rp = (unsigned char *)vbuf;
      while(*rp != '\0'){
        const unsigned char *pp;
        for(pp = sp; pp < ep; pp++, rp++){
          if(*pp != *rp) break;
        }
        if(pp == ep && (*rp <= ' ' || *rp == ',')){
          hit = true;
          break;
        }
        while(*rp > ' ' && *rp != ','){
          rp++;
        }
        while((*rp != '\0' && *rp <= ' ') || *rp == ','){
          rp++;
        }
      }
      if(!hit) return false;
    }
    sp = ep;
  }
  return true;
}


/* Check whether a string includes at least one token in another string.
 `vbuf' specifies the target value.
 `expr' specifies the operation value.
 If they matches, the return value is true, else it is false. */
static bool tcDBCheckStrOr(const char *vbuf, const char *expr){
  assert(vbuf && expr);
  const unsigned char *sp = (unsigned char *)expr;
  while(*sp != '\0'){
    while((*sp != '\0' && *sp <= ' ') || *sp == ','){
      sp++;
    }
    const unsigned char *ep = sp;
    while(*ep > ' ' && *ep != ','){
      ep++;
    }
    if(ep > sp){
      bool hit = false;
      const unsigned char *rp = (unsigned char *)vbuf;
      while(*rp != '\0'){
        const unsigned char *pp;
        for(pp = sp; pp < ep; pp++, rp++){
          if(*pp != *rp) break;
        }
        if(pp == ep && (*rp <= ' ' || *rp == ',')){
          hit = true;
          break;
        }
        while(*rp > ' ' && *rp != ','){
          rp++;
        }
        while((*rp != '\0' && *rp <= ' ') || *rp == ','){
          rp++;
        }
      }
      if(hit) return true;
    }
    sp = ep;
  }
  return false;
}


/* Check whether a string is equal to at least one token in another string.
 `vbuf' specifies the target value.
 `expr' specifies the operation value.
 If they matches, the return value is true, else it is false. */
static bool tcCheckStrEquAtLeastOnce(const char *vbuf, const char *expr){
  assert(vbuf && expr);
  const unsigned char *sp = (unsigned char *)expr;
  while(*sp != '\0'){
    while((*sp != '\0' && *sp <= ' ') || *sp == ','){
      sp++;
    }
    const unsigned char *ep = sp;
    while(*ep > ' ' && *ep != ','){
      ep++;
    }
    if(ep > sp){
      const unsigned char *rp;
      for(rp = (unsigned char *)vbuf; *rp != '\0'; rp++){
        if(*sp != *rp || sp >= ep) break;
        sp++;
      }
      if(*rp == '\0' && sp == ep) return true;
    }
    sp = ep;
  }
  return false;
}

/* Check matching of a operand expression and a column value.
 `cond' specifies the condition object.
 `vbuf' specifies the column value.
 `vsiz' specifies the size of the column value.
 If they matches, the return value is true, else it is false. */
static bool tcDBQueryCondMatch(TDBQueryCondition *cond, const char *vbuf, int vsiz){
  assert(cond && vbuf && vsiz >= 0);
  bool hit = false;
  switch(cond->op){
    case TDBQCSTREQ:
      hit = vsiz == cond->esiz && !memcmp(vbuf, cond->expr, cond->esiz);
      break;
    case TDBQCSTRINC:
      hit = strstr(vbuf, cond->expr) != NULL;
      break;
    case TDBQCSTRBW:
      hit = tcstrfwm(vbuf, cond->expr);
      break;
    case TDBQCSTREW:
      hit = tcstrbwm(vbuf, cond->expr);
      break;
    case TDBQCSTRAND:
      hit = tcDBCheckStrAnd(vbuf, cond->expr);
      break;
    case TDBQCSTROR:
      hit = tcDBCheckStrOr(vbuf, cond->expr);
      break;
    case TDBQCSTROREQ:
      hit = tcCheckStrEquAtLeastOnce(vbuf, cond->expr);
      break;
    case TDBQCSTRRX:
      hit = cond->regex && regexec(cond->regex, vbuf, 0, NULL, 0) == 0;
      break;
    case TDBQCNUMEQ:
      hit = tcDBValueToFloat(vbuf, vsiz) == tcDBatof(cond->expr);
      break;
    case TDBQCNUMGT:
      hit = tcDBValueToFloat(vbuf, vsiz) > tcDBatof(cond->expr);
      break;
    case TDBQCNUMGE:
      //printf("%Lf=%Lf", tcDBValueToFloat(vbuf, vsiz), tcDBatof(cond->expr));
      hit = tcDBValueToFloat(vbuf, vsiz) >= tcDBatof(cond->expr);
      break;
    case TDBQCNUMLT:
      hit = tcDBValueToFloat(vbuf, vsiz) < tcDBatof(cond->expr);
      break;
    case TDBQCNUMLE:
      hit = tcDBValueToFloat(vbuf, vsiz) <= tcDBatof(cond->expr);
      break;
    case TDBQCNUMBT:
      hit = tcDBCheckValueBetween(tcDBValueToFloat(vbuf, vsiz), cond->expr);
      break;
    case TDBQCNUMOREQ:
      hit = tcDBCheckValueEquAtLeastOnce(tcDBValueToFloat(vbuf, vsiz), cond->expr);
      break;
  }
  return hit;
}

/* Check matching of one condition and a record.
 `qry' specifies the query object.
 `cond' specifies the condition object.
 `pkbuf' specifies the pointer to the region of the primary key.
 `pksiz' specifies the size of the region of the primary key.
 If they matches, the return value is true, else it is false. */
static bool tcDBQueryOneCondMatch(TDBQuery *qry, TDBQueryCondition *cond, const char *pkbuf, int pksiz){
  assert(qry && cond && pkbuf && pksiz >= 0);
  bool rv;
  int vsiz;
  char *vbuf = tcDBGetFieldValueStr(qry->DB, pkbuf, pksiz, cond->ColumnName, cond->ColumnNameSize, &vsiz);
  if(vbuf){
    rv = tcDBQueryCondMatch(cond, vbuf, vsiz) == cond->sign;
    TCFREE(vbuf);
  } else {
    rv = !cond->sign;
  }
  return rv;
}

/* Check matching of all conditions and a record.
 `qry' specifies the query object.
 `pkbuf' specifies the pointer to the region of the primary key.
 `pksiz' specifies the size of the region of the primary key.
 If they matches, the return value is true, else it is false. */
static bool tcDBQueryAllCondMatch(TDBQuery *qry, const char *pkbuf, int pksiz){
  assert(qry && pkbuf && pksiz >= 0);
  TBTreeDatabase *tdb = qry->DB;
  TDBQueryCondition *conds = qry->Conditions;
  int cnum = qry->ConditionCount;
  TCMAP *cols = tcDBGetColumnValuesMapImpl(tdb, pkbuf, pksiz);
  if(!cols) return false;
  bool ok = true;
  for(int i = 0; i < cnum; i++){
    TDBQueryCondition *cond = conds + i;
    if(!cond->alive) continue;
      int vsiz;
      const char *vbuf = tcmapget(cols, cond->ColumnName, cond->ColumnNameSize, &vsiz);
      if(vbuf){
        if(tcDBQueryCondMatch(cond, vbuf, vsiz) != cond->sign){
          ok = false;
          break;
        }
      } else {
        if(cond->sign){
          ok = false;
          break;
        }
      }
  }
  tcmapdel(cols);
  return ok;
}

static inline int GetIndexKeyNameBW(char *aIdxName, const char *aFieldName, const int aFieldNameSize, const char *aFieldValue, const int aFieldValueSize) {
  //aIdxName = IDXKEY;          
  char * vP = aIdxName;       
  memcpy(vP, IDXKEY, strlen(IDXKEY)); 
  vP += strlen(IDXKEY);       
  memcpy(vP, aFieldName, aFieldNameSize); 
  vP += aFieldNameSize;                 
  memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR)); 
  vP += strlen(KEYSEPSTR);                       
  memcpy(vP, aFieldValue, aFieldValueSize); 
  vP += aFieldValueSize;                 
  return vP-aIdxName;
}

//return the size of the index key name and the aIdxName. u must ensure the aIdxName hass enough space to store.
static inline int GetIndexKeyName(char *aIdxName, const char *aFieldName, const int aFieldNameSize, const char *aFieldValue, const int aFieldValueSize) {
  //aIdxName = IDXKEY;          
  char * vP = aIdxName;       
  memcpy(vP, IDXKEY, strlen(IDXKEY)); 
  vP += strlen(IDXKEY);       
  memcpy(vP, aFieldName, aFieldNameSize); 
  vP += aFieldNameSize;                 
  memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR)); 
  vP += strlen(KEYSEPSTR);                       
  memcpy(vP, aFieldValue, aFieldValueSize); 
  vP += aFieldValueSize;
  memcpy(vP, FIELDVALSEPSTR, FIELDVALSEPSTRSIZE); 
  vP += FIELDVALSEPSTRSIZE;                       
  return vP-aIdxName;
}

/* Jump a cursor to the record of a key.
 `cur' specifies the cursor object.
 `expr' specifies the expression.
 `esiz' specifies the size of the expression.
 `first' specifies whether to jump the first candidate.
 If successful, the return value is true, else, it is false. */
static bool tcDBQueryIdxCurJumpNum(BDBCUR *cur, const char *aFieldName, const int aFieldNameSize, const char *expr, int esiz, bool first){
  assert(cur && expr && esiz >= 0);
  char stack[MAXKEYLEN+1];
  int vsiz;
  if (*aFieldName != '\0') {
    vsiz = strlen(IDXKEY) +  aFieldNameSize + strlen(IDXKEY) + FIELDVALSEPSTRSIZE + esiz;
  }
  else {
    vsiz = strlen(KEYSEPSTR) + esiz;
  }
  char *rbuf, *vP;
  //char stack[TCNUMBUFSIZ], *rbuf;
  if(vsiz < sizeof(stack)){
    rbuf = stack;
  } else {
    TCMALLOC(rbuf, vsiz);
  }
  vP = rbuf;
  if (*aFieldName != '\0') {
    memcpy(vP, IDXKEY, strlen(IDXKEY));
    vP += strlen(IDXKEY);
    memcpy(vP, aFieldName, aFieldNameSize);
    vP += aFieldNameSize;
    memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR)); 
    vP += strlen(KEYSEPSTR);                       
    memcpy(vP, expr, esiz);
    vP[0] = first ? 0x00 : 0x7f;
    vP += esiz;
    memcpy(vP, FIELDVALSEPSTR, FIELDVALSEPSTRSIZE);
    vP += FIELDVALSEPSTRSIZE;
    //memcpy(rbuf + 1, expr, esiz);
  }
  else {
    memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR));
    vP += strlen(KEYSEPSTR);
    memcpy(vP, expr, esiz);
    vP[0] = first ? 0x00 : 0x7f;
    vP += esiz;
  }

  bool err = false;
  if(first){
    if(!tcbdbcurjump(cur, rbuf, vsiz)) err = true;
  } else {
    if(!tcbdbcurjumpback(cur, rbuf, vsiz)) err = true;
  }
  if(rbuf != stack) TCFREE(rbuf);
  return !err;
}

#define GET_INDEX_KEY_NAME_BW(aIdxName, aFieldName, aFieldNameSize, aFieldValue, aFieldValueSize) \
char * vP = vIdxName;       \
memcpy(vP, IDXKEY, strlen(IDXKEY)); \
vP += strlen(IDXKEY);       \
memcpy(vP, aFieldName, aFieldNameSize);   \
vP += aFieldNameSize;                     \
memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR)); \
vP += strlen(KEYSEPSTR);                  \
memcpy(vP, aFieldValue, aFieldValueSize); \
vP += aFieldValueSize;                    \
aFieldValueSize = vP-vIdxName;

#define GET_INDEX_KEY_NAME(aIdxName, aFieldName, aFieldNameSize, aFieldValue, aFieldValueSize) \
char * vP = vIdxName;       \
memcpy(vP, IDXKEY, strlen(IDXKEY)); \
vP += strlen(IDXKEY);       \
memcpy(vP, aFieldName, aFieldNameSize);   \
vP += aFieldNameSize;                     \
memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR)); \
vP += strlen(KEYSEPSTR);                  \
memcpy(vP, aFieldValue, aFieldValueSize); \
vP += aFieldValueSize;                    \
memcpy(vP, FIELDVALSEPSTR, FIELDVALSEPSTRSIZE); \
vP += FIELDVALSEPSTRSIZE;                       \
aFieldValueSize = vP-vIdxName;

/* Fetch record keys from an index matching to a condition.
 `qry' specifies the query object.
 `cond' specifies the condition object.
 `idx' specifies an index object.
 The return value is a map object containing primary keys of the corresponding records. */
static TCMAP *tcDBQueryIdxFetch(TDBQuery *qry, TDBQueryCondition *cond){
  assert(qry && cond);
  TBTreeDatabase *vDB = qry->DB;
  //TCBDB *hdb = vDB->DB;
  TCXSTR *hint = qry->hint;
  const char *expr = cond->expr;
  int esiz = cond->esiz;
  //bool trim = *cond->ColumnName != '\0';
  bool vIsPKIndex = *cond->ColumnName == '\0';
  TCMAP *nmap = tcmapnew2(tclmin(TDBDEFBNUM, tcDBGetRecordNumImpl(vDB)) / 4 + 1);
  if(cond->op == TDBQCSTREQ){
    tcxstrprintf(hint, "using an auxiliary index: \"%s\" one (STREQ)\n", cond->ColumnName);
    BDBCUR *cur;// = tcbdbcurnew(vDB->IndexDB);
    char vIdxName[MAXKEYLEN+1];
    if (vIsPKIndex) {
      cur = tcbdbcurnew(vDB->DB);
      char *vP = vIdxName;
      memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR));
      vP += strlen(KEYSEPSTR);
      memcpy(vP, expr, esiz);
      esiz += strlen(KEYSEPSTR);
    }
    else {
      cur = tcbdbcurnew(vDB->IndexDB);
      GET_INDEX_KEY_NAME(vIdxName, cond->ColumnName, cond->ColumnNameSize, expr, esiz);
    }
    tcbdbcurjump(cur, vIdxName, esiz);
    const char *kbuf;
    int ksiz;
    while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
      //if(trim) ksiz -= 3;
      if (vIsPKIndex && ksiz != esiz) continue;
      if(ksiz > esiz && !memcmp(kbuf, vIdxName, esiz)){
        int vsiz;
        const char *vbuf = tcbdbcurval3(cur, &vsiz);
        tcmapputkeep(nmap, vbuf, vsiz, "", 0);
      } else {
        break;
      }
      tcbdbcurnext(cur);
    }
    tcbdbcurdel(cur);
  } else if(cond->op == TDBQCSTRBW){
    tcxstrprintf(hint, "using an auxiliary index: \"%s\" asc (STRBW)\n", cond->ColumnName);
    BDBCUR *cur;// = tcbdbcurnew(vDB->IndexDB);
    char vIdxName[MAXKEYLEN+1];
    if (vIsPKIndex) {
      cur = tcbdbcurnew(vDB->DB);
      char *vP = vIdxName;
      memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR));
      vP += strlen(KEYSEPSTR);
      memcpy(vP, expr, esiz);
      esiz += strlen(KEYSEPSTR);
    }
    else {
      cur = tcbdbcurnew(vDB->IndexDB);
      GET_INDEX_KEY_NAME_BW(vIdxName, cond->ColumnName, cond->ColumnNameSize, expr, esiz);
    }
    tcbdbcurjump(cur, vIdxName, esiz);
    const char *kbuf;
    int ksiz;
    while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
      //if(trim) ksiz -= 3;
      if(ksiz >= esiz && !memcmp(kbuf, vIdxName, esiz)){
        int vsiz;
        const char *vbuf = tcbdbcurval3(cur, &vsiz);
        tcmapputkeep(nmap, vbuf, vsiz, "", 0);
      } else {
        break;
      }
      tcbdbcurnext(cur);
    }
    tcbdbcurdel(cur);
  } else if(cond->op == TDBQCSTROREQ){
    tcxstrprintf(hint, "using an auxiliary index: \"%s\" skip (STROREQ)\n", cond->ColumnName);
    TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,");
    tclistsort(tokens);
    for(int i = 1; i < TCLISTNUM(tokens); i++){ //remove duplication items.
      if(!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))){
        TCFREE(tclistremove2(tokens, i));
        i--;
      }
    }
    int tnum = TCLISTNUM(tokens);
    char vIdxName[MAXKEYLEN+1];
    for(int i = 0; i < tnum; i++){
      const char *token;
      int tsiz;
      TCLISTVAL(token, tokens, i, tsiz);
      if(tsiz < 1) continue;
      BDBCUR *cur;// = tcbdbcurnew(vDB->IndexDB);
      if (vIsPKIndex) {
        cur = tcbdbcurnew(vDB->DB);
        char *vP = vIdxName;
        memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR));
        vP += strlen(KEYSEPSTR);
        memcpy(vP, token, tsiz);
        tsiz += strlen(KEYSEPSTR);
      }
      else {
        cur = tcbdbcurnew(vDB->IndexDB);
        GET_INDEX_KEY_NAME(vIdxName, cond->ColumnName, cond->ColumnNameSize, token, tsiz);
      }
      tcbdbcurjump(cur, vIdxName, tsiz);
      const char *kbuf;
      int ksiz;
      while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
        //if(trim) ksiz -= 3;
        if (vIsPKIndex && ksiz != tsiz) continue;
        if(ksiz > tsiz && !memcmp(kbuf, vIdxName, tsiz)){
          int vsiz;
          const char *vbuf = tcbdbcurval3(cur, &vsiz);
          tcmapputkeep(nmap, vbuf, vsiz, "", 0);
        } else {
          break;
        }
        tcbdbcurnext(cur);
      }
      tcbdbcurdel(cur);
    }
    tclistdel(tokens);
  } else if(cond->op == TDBQCNUMEQ){
    tcxstrprintf(hint, "using an auxiliary index: \"%s\" asc (NUMEQ)\n", cond->ColumnName);
    long double xnum = tcDBatof(expr);
    BDBCUR *cur;// = tcbdbcurnew(vDB->IndexDB);
    if (vIsPKIndex) {
      cur = tcbdbcurnew(vDB->DB);
    }
    else {
      cur = tcbdbcurnew(vDB->IndexDB);
    }
    tcDBQueryIdxCurJumpNum(cur, cond->ColumnName, cond->ColumnNameSize, expr, esiz, true);
    const char *kbuf;
    int ksiz;
    while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
      if(tcDBatof(GetFieldValueFromIndexKey(kbuf, cond->ColumnNameSize)) == xnum){
        int vsiz;
        const char *vbuf = tcbdbcurval3(cur, &vsiz);
        tcmapputkeep(nmap, vbuf, vsiz, "", 0);
      } else {
        break;
      }
      tcbdbcurnext(cur);
    }
    tcbdbcurdel(cur);
  } else if(cond->op == TDBQCNUMGT || cond->op == TDBQCNUMGE){
    tcxstrprintf(hint, "using an auxiliary index: \"%s\" asc (NUMGT/NUMGE)\n", cond->ColumnName);
    long double xnum = tcDBatof(expr);
    BDBCUR *cur;// = tcbdbcurnew(vDB->IndexDB);
    if (vIsPKIndex) {
      cur = tcbdbcurnew(vDB->DB);
    }
    else {
      cur = tcbdbcurnew(vDB->IndexDB);
    }
    tcDBQueryIdxCurJumpNum(cur, cond->ColumnName, cond->ColumnNameSize, expr, esiz, true);
    const char *kbuf;
    int ksiz;
    while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
      long double knum = tcDBatof(GetFieldValueFromIndexKey(kbuf, cond->ColumnNameSize));
      if(knum > xnum || (knum >= xnum && cond->op == TDBQCNUMGE)){
        int vsiz;
        const char *vbuf = tcbdbcurval3(cur, &vsiz);
        tcmapputkeep(nmap, vbuf, vsiz, "", 0);
      }
      tcbdbcurnext(cur);
    }
    tcbdbcurdel(cur);
  } else if(cond->op == TDBQCNUMLT || cond->op == TDBQCNUMLE){
    tcxstrprintf(hint, "using an auxiliary index: \"%s\" desc (NUMLT/NUMLE)\n", cond->ColumnName);
    long double xnum = tcDBatof(expr);
    BDBCUR *cur;// = tcbdbcurnew(vDB->IndexDB);
    if (vIsPKIndex) {
      cur = tcbdbcurnew(vDB->DB);
    }
    else {
      cur = tcbdbcurnew(vDB->IndexDB);
    }
    tcDBQueryIdxCurJumpNum(cur, cond->ColumnName, cond->ColumnNameSize, expr, esiz, false);
    const char *kbuf;
    int ksiz;
    while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
      long double knum = tcDBatof(GetFieldValueFromIndexKey(kbuf, cond->ColumnNameSize));
      if(knum < xnum || (knum <= xnum && cond->op == TDBQCNUMLE)){
        int vsiz;
        const char *vbuf = tcbdbcurval3(cur, &vsiz);
        tcmapputkeep(nmap, vbuf, vsiz, "", 0);
      }
      tcbdbcurprev(cur);
    }
    tcbdbcurdel(cur);
  } else if(cond->op == TDBQCNUMBT){
    tcxstrprintf(hint, "using an auxiliary index: \"%s\" asc (NUMBT)\n", cond->ColumnName);
    while(*expr == ' ' || *expr == ','){
      expr++;
    }
    const char *pv = expr;
    while(*pv != '\0' && *pv != ' ' && *pv != ','){
      pv++;
    }
    esiz = pv - expr;
    if(*pv != ' ' && *pv != ',') pv = " ";
    pv++;
    while(*pv == ' ' || *pv == ','){
      pv++;
    }
    long double lower = tcDBatof(expr);
    long double upper = tcDBatof(pv);
    if(lower > upper){
      expr = pv;
      esiz = strlen(expr);
      long double swap = lower;
      lower = upper;
      upper = swap;
    }
    BDBCUR *cur;// = tcbdbcurnew(vDB->IndexDB);
    if (vIsPKIndex) {
      cur = tcbdbcurnew(vDB->DB);
    }
    else {
      cur = tcbdbcurnew(vDB->IndexDB);
    }
    tcDBQueryIdxCurJumpNum(cur, cond->ColumnName, cond->ColumnNameSize, expr, esiz, true);
    const char *kbuf;
    int ksiz;
    while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
      if(tcDBatof(GetFieldValueFromIndexKey(kbuf, cond->ColumnNameSize)) > upper) break;
      int vsiz;
      const char *vbuf = tcbdbcurval3(cur, &vsiz);
      tcmapputkeep(nmap, vbuf, vsiz, "", 0);
      tcbdbcurnext(cur);
    }
    tcbdbcurdel(cur);
  } else if(cond->op == TDBQCNUMOREQ){
    tcxstrprintf(hint, "using an auxiliary index: \"%s\" skip (NUMOREQ)\n", cond->ColumnName);
    BDBCUR *cur;// = tcbdbcurnew(vDB->IndexDB);
    if (vIsPKIndex) {
      cur = tcbdbcurnew(vDB->DB);
    }
    else {
      cur = tcbdbcurnew(vDB->IndexDB);
    }
    TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,");
    tclistsortex(tokens, tcDBCmpPkeyNumAsc);
    for(int i = 1; i < TCLISTNUM(tokens); i++){
      if(tcDBatof(TCLISTVALPTR(tokens, i)) == tcDBatof(TCLISTVALPTR(tokens, i - 1))){
        TCFREE(tclistremove2(tokens, i));
        i--;
      }
    }
    int tnum = TCLISTNUM(tokens);
    for(int i = 0; i < tnum; i++){
      const char *token;
      int tsiz;
      TCLISTVAL(token, tokens, i, tsiz);
      if(tsiz < 1) continue;
      long double xnum = tcDBatof(token);
      tcDBQueryIdxCurJumpNum(cur, cond->ColumnName, cond->ColumnNameSize, token, tsiz, true);
      const char *kbuf;
      int ksiz;
      while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
        if(tcDBatof(GetFieldValueFromIndexKey(kbuf, cond->ColumnNameSize)) == xnum){
          int vsiz;
          const char *vbuf = tcbdbcurval3(cur, &vsiz);
          tcmapputkeep(nmap, vbuf, vsiz, "", 0);
        } else {
          break;
        }
        tcbdbcurnext(cur);
      }
    }
    tclistdel(tokens);
    tcbdbcurdel(cur);
  }
  tcxstrprintf(hint, "auxiliary result set size: %lld\n", (long long)TCMAPRNUM(nmap));
  return nmap;
}

/* Retrieve and remove each record corresponding to a query object.
 `pkbuf' specifies the pointer to the region of the primary key.
 `pksiz' specifies the size of the region of the primary key.
 `cols' specifies a map object containing columns and values.
 `op' specifies the pointer to the optional opaque object.
 The return value is flags of the post treatment by bitwise-or.
 If successful, the return value is true, else, it is false. */
static int tcDBQueryGetOutProc(const void *pkbuf, int pksiz, TCMAP *cols, void *op){
  TCLIST *rv = ((void **)op)[0];
  TCLIST *cnames = ((void **)op)[1];
  int cnnum = TCLISTNUM(cnames);
  tcmapput(cols, "", 0, pkbuf, pksiz);
  tcmapmove(cols, "", 0, true);
  if(cnnum > 0){
    TCMAP *ncols = tcmapnew2(cnnum + 1);
    for(int j = 0; j < cnnum; j++){
      const char *cname;
      int cnsiz;
      TCLISTVAL(cname, cnames, j, cnsiz);
      int cvsiz;
      const char *cvalue = tcmapget(cols, cname, cnsiz, &cvsiz);
      if(cvalue) tcmapput(ncols, cname, cnsiz, cvalue, cvsiz);
    }
    int csiz;
    char *cbuf = tcstrjoin4(ncols, &csiz);
    tclistpushmalloc(rv, cbuf, csiz);
    tcmapdel(ncols);
  } else {
    int csiz;
    char *cbuf = tcstrjoin4(cols, &csiz);
    tclistpushmalloc(rv, cbuf, csiz);
  }
// */
  return TDBQPOUT;
}
#define DBCURJUMPBW(aCur, aFieldName, aFieldNameSize, aIsPK) \
if (aIsPK) { \
  aCur = tcbdbcurnew(vDB->DB); \
char vIdxName[MAXKEYLEN+1] = KEYSEPSTR;    \
char *vP = vIdxName;                    \
vP += strlen(KEYSEPSTR);                   \
memcpy(vP, aFieldName, aFieldNameSize); \
vP += aFieldNameSize;                   \
tcbdbcurjump(aCur, vIdxName, vP-vIdxName); \
} \
else { \
  aCur = tcbdbcurnew(vDB->IndexDB); \
  char vIdxName[MAXKEYLEN+1] = IDXKEY;    \
  char *vP = vIdxName;                    \
  vP += strlen(IDXKEY);                   \
  memcpy(vP, aFieldName, aFieldNameSize); \
  vP += aFieldNameSize;                   \
  memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR));             \
  vP += strlen(KEYSEPSTR);                              \
  tcbdbcurjump(aCur, vIdxName, vP-vIdxName); \
}

#define DBCURJUMPBACKBW(aCur, aFieldName, aFieldNameSize, aIsPK) \
if (aIsPK) {               \
  aCur = tcbdbcurnew(vDB->DB); \
  char vIdxName[MAXKEYLEN+1] = KEYSEPSTR;    \
  char *vP = vIdxName;                    \
  vP += strlen(KEYSEPSTR);                   \
  memcpy(vP, aFieldName, aFieldNameSize); \
  vP += aFieldNameSize;                   \
  tcbdbcurjumpback(aCur, vIdxName, vP-vIdxName);        \
}                           \
else {                      \
  aCur = tcbdbcurnew(vDB->IndexDB); \
  char vIdxName[MAXKEYLEN+1] = IDXKEY;    \
  char *vP = vIdxName;                    \
  vP += strlen(IDXKEY);                   \
  memcpy(vP, aFieldName, aFieldNameSize); \
  vP += aFieldNameSize;                   \
  memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR));             \
  vP += strlen(KEYSEPSTR);                              \
  tcbdbcurjumpback(aCur, vIdxName, vP-vIdxName);        \
}

/* Process each record corresponding to a query object with non-atomic fashion. */
bool tcDBQueryProc2(TDBQuery *qry, TDBQRYPROC proc, void *op){
  assert(qry && proc);
  TBTreeDatabase *tdb = qry->DB;
  TDBQueryCondition *conds = qry->Conditions;
  int cnum = qry->ConditionCount;
  bool err = false;
  int64_t getnum = 0;
  int64_t putnum = 0;
  int64_t outnum = 0;
  TCLIST *res = tcDBQuerySearch(qry);
  int rnum = TCLISTNUM(res);
  for(int i = 0; i < rnum; i++){
    if(!DBLOCKMETHOD(tdb, true)){
      err = true;
      break;
    }
    if(!tdb->Opened || !tdb->Writable){
      tcDBRaiseError(tdb, TCEINVALID, __FILE__, __LINE__, __func__);
      DBUNLOCKMETHOD(tdb);
      err = true;
      break;
    }
    int pksiz;
    const char *pkbuf;
    TCLISTVAL(pkbuf, res, i, pksiz);
    TCMAP *cols = tcDBGetColumnValuesMapImpl(tdb, pkbuf, pksiz);
    if(cols){
      getnum++;
      bool ok = true;
      for(int j = 0; j < cnum; j++){
        TDBQueryCondition *cond = conds + j;
          int vsiz;
          const char *vbuf = tcmapget(cols, cond->ColumnName, cond->ColumnNameSize, &vsiz);
          if(vbuf){
            if(tcDBQueryCondMatch(cond, vbuf, vsiz) != cond->sign){
              ok = false;
              break;
            }
          } else {
            if(cond->sign){
              ok = false;
              break;
            }
          }
      }
      if(ok){
        int flags = proc(pkbuf, pksiz, cols, op);
        if(flags & TDBQPPUT){
          if(tcDBPutColumnsImpl(tdb, pkbuf, pksiz, cols, TDBPDOVER)){
            putnum++;
          } else {
            err = true;
          }
        } else if(flags & TDBQPOUT){
          if(tcDBDeleteImpl(tdb, pkbuf, pksiz, tcbdbout)){
            outnum++;
          } else {
            err = true;
          }
        }
        if(flags & TDBQPSTOP) i = rnum;
      }
      tcmapdel(cols);
    }
    DBUNLOCKMETHOD(tdb);
  }
  tclistdel(res);
  tcxstrprintf(qry->hint, "post treatment: get=%lld, put=%lld, out=%lld\n",
               (long long)getnum, (long long)putnum, (long long)outnum);
  return !err;
}

/* Execute the search of a query object.
 `qry' specifies the query object.
 The return value is a list object of the primary keys of the corresponding records. */
static TCLIST *tcDBQuerySearchImpl(TDBQuery *qry){
  assert(qry);
  TDBQueryCondition *conds = qry->Conditions;
  TBTreeDatabase *vDB = qry->DB;
  int cnum = qry->ConditionCount;
  int acnum = cnum;
  int max = qry->max;
  if(max < INT_MAX - qry->skip) max += qry->skip;
  const char *oname = qry->OrderColumnName;
  int otype = qry->OrderType;
  TCXSTR *hint = qry->hint;
  TCLIST *res = NULL;
  for(int i = 0; i < cnum; i++){
    TDBQueryCondition *cond = conds + i;
    cond->alive = true;
  }
  tcxstrclear(hint);
  bool isord = oname != NULL;
  TDBQueryCondition *mcond = NULL; //the Main Condition with index
  TDBQueryCondition *ncond = NULL; //the Second Condition with index
  TDBQueryCondition *scond = NULL; //the Third Condition with index
  for(int i = 0; i < cnum; i++) {
    TDBQueryCondition *cond = conds + i;
    if(!cond->sign || cond->noidx) continue;
    int vIndexType = tcDBGetFieldIndexType(vDB, cond->ColumnName, cond->ColumnNameSize);
    cond->IndexType = vIndexType;
    if (vIndexType != TDBITVOID) {
      switch(vIndexType){
        case TDBITLEXICAL:
          switch(cond->op){
            case TDBQCSTREQ:
            case TDBQCSTRBW:
            case TDBQCSTROREQ:
              if(!mcond){
                mcond = cond;
              } else if(!ncond){
                ncond = cond;
              }
              break;
            default:
              if(!scond){
                scond = cond;
              }
              break;
          }
          break;
        case TDBITDECIMAL:
          switch(cond->op){
            case TDBQCNUMEQ:
            case TDBQCNUMGT:
            case TDBQCNUMGE:
            case TDBQCNUMLT:
            case TDBQCNUMLE:
            case TDBQCNUMBT:
            case TDBQCNUMOREQ:
              if(!mcond){
                mcond = cond;
              } else if(!ncond){
                ncond = cond;
              }
              break;
            default:
              if(!scond){
                scond = cond;
              }
              break;
          }
          break;
        case TDBITTOKEN:
          switch(cond->op){
            case TDBQCSTRAND:
            case TDBQCSTROR:
              if(!mcond){
                mcond = cond;
              } else if(!ncond){
                ncond = cond;
              }
              break;
          }
          break;
        case TDBITQGRAM:
          switch(cond->op){
            case TDBQCFTSPH:
              if(!mcond){
                mcond = cond;
              } else if(!ncond){
                ncond = cond;
              }
              break;
          }
          break;
      }
    }
  }//for loop to determine the main, second, third cond.
  if(mcond){
    //printf("mcond=%d,ncond=%d\n", mcond!=NULL, ncond!=NULL);
    res = tclistnew();
    mcond->alive = false;
    acnum--;
    TCMAP *nmap = NULL;
    if(ncond){
      ncond->alive = false;
      acnum--;
      nmap = tcDBQueryIdxFetch(qry, ncond);
      max = tclmin(max, TCMAPRNUM(nmap));
    }
    const char *expr = mcond->expr;
    int esiz = mcond->esiz;
    TDBQueryCondition *ucond = NULL;
    for(int i = 0; i < cnum; i++){
      TDBQueryCondition *cond = conds + i;
      if(!cond->alive) continue;
      if(ucond){
        ucond = NULL;
        break;
      }
      ucond = cond;
    }
    //bool trim = *midx->name != '\0';
    bool vIsPKIndex = *mcond->ColumnName == '\0';
    if(mcond->op == TDBQCSTREQ){
      tcxstrprintf(hint, "using an index: \"%s\" asc (STREQ)\n", mcond->ColumnName);
#if defined(DEBUG)
      fprintf(stderr, "using an index: \"%s\" asc (STREQ)\n", mcond->ColumnName);
#endif
      BDBCUR *cur;
      char vIdxName[MAXKEYLEN+1];
      if (vIsPKIndex) {
        cur = tcbdbcurnew(vDB->DB);
        char *vP = vIdxName;
        memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR));
        vP += strlen(KEYSEPSTR);
        memcpy(vP, expr, esiz);
        esiz = strlen(KEYSEPSTR)+esiz;
      }
      else {
        cur = tcbdbcurnew(vDB->IndexDB);
        GET_INDEX_KEY_NAME(vIdxName, mcond->ColumnName, mcond->ColumnNameSize, expr, esiz);
      }
      tcbdbcurjump(cur, vIdxName, esiz);
      if(oname && !strcmp(oname, mcond->ColumnName)) oname = NULL;
      bool all = oname != NULL;
      if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max);
      //trim = *midx->name != '\0';
      const char *kbuf;
      int ksiz;
      while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
        //if(trim) ksiz -= 3;
        if (vIsPKIndex && ksiz != esiz) continue;
        if(ksiz > esiz && !memcmp(kbuf, vIdxName, esiz)){
          int vsiz;
          const char *vbuf = tcbdbcurval3(cur, &vsiz);
          int nsiz;
          if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){
            if(acnum < 1){
              TCLISTPUSH(res, vbuf, vsiz);
            } else if(ucond){
              if(tcDBQueryOneCondMatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz);
            } else if(tcDBQueryAllCondMatch(qry, vbuf, vsiz)){
              TCLISTPUSH(res, vbuf, vsiz);
            }
          }
        } else {
          break;
        }
        tcbdbcurnext(cur);
      }
      tcbdbcurdel(cur);
    } else if(mcond->op == TDBQCSTRBW){
      tcxstrprintf(hint, "using an index: \"%s\" asc (STRBW)\n", mcond->ColumnName);
#if defined(DEBUG)
      fprintf(stderr, "using an index: \"%s\" asc (STRBW)\n", mcond->ColumnName);
#endif
      BDBCUR *cur;
      char vIdxName[MAXKEYLEN+1];
      if (vIsPKIndex) {
        cur = tcbdbcurnew(vDB->DB);
        char *vP = vIdxName;
        memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR));
        vP += strlen(KEYSEPSTR);
        memcpy(vP, expr, esiz);
        esiz = strlen(KEYSEPSTR)+esiz;
      }
      else {
        cur = tcbdbcurnew(vDB->IndexDB);
        GET_INDEX_KEY_NAME_BW(vIdxName, mcond->ColumnName, mcond->ColumnNameSize, expr, esiz);
      }
      tcbdbcurjump(cur, vIdxName, esiz);
      bool all = oname && (strcmp(oname, mcond->ColumnName) || otype != TDBQOSTRASC);
      if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max);
      const char *kbuf;
      int ksiz;
      while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
        //if(trim) ksiz -= 3;
        if (vIsPKIndex && ksiz < esiz) continue;
        if(ksiz >= esiz && !memcmp(kbuf, vIdxName, esiz)){
          int vsiz;
          const char *vbuf = tcbdbcurval3(cur, &vsiz);
          int nsiz;
          if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){
            if(acnum < 1){
              TCLISTPUSH(res, vbuf, vsiz);
            } else if(ucond){
              if(tcDBQueryOneCondMatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz);
            } else if(tcDBQueryAllCondMatch(qry, vbuf, vsiz)){
              TCLISTPUSH(res, vbuf, vsiz);
            }
          }
        } else {
          break;
        }
        tcbdbcurnext(cur);
      }
      tcbdbcurdel(cur);
      if(oname && !strcmp(oname, mcond->ColumnName)){
        if(otype == TDBQOSTRASC){
          oname = NULL;
        } else if(otype == TDBQOSTRDESC){
          tclistinvert(res);
          oname = NULL;
        }
      }
    } else if(mcond->op == TDBQCSTROREQ){
      tcxstrprintf(hint, "using an index: \"%s\" skip (STROREQ)\n", mcond->ColumnName);
#if defined(DEBUG)
      fprintf(stderr, "using an index: \"%s\" skip (STROREQ)\n", mcond->ColumnName);
#endif
      BDBCUR *cur;
      if (vIsPKIndex) {
        cur = tcbdbcurnew(vDB->DB);
      }
      else {
        cur = tcbdbcurnew(vDB->IndexDB);
      }
      TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,");
      tclistsort(tokens);
      for(int i = 1; i < TCLISTNUM(tokens); i++){
        if(!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))){
          TCFREE(tclistremove2(tokens, i));
          i--;
        }
      }
      if(oname && !strcmp(oname, mcond->ColumnName)){
        if(otype == TDBQOSTRASC){
          oname = NULL;
        } else if(otype == TDBQOSTRDESC){
          tclistinvert(tokens);
          oname = NULL;
        }
      }
      int tnum = TCLISTNUM(tokens);
      bool all = oname != NULL;
      if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max);
      char vIdxName[MAXKEYLEN+1];
      for(int i = 0; (all || TCLISTNUM(res) < max) && i < tnum; i++){
        const char *token;
        int tsiz;
        TCLISTVAL(token, tokens, i, tsiz);
        if(tsiz < 1) continue;
        if (vIsPKIndex) {
          char *vP = vIdxName;
          memcpy(vP, KEYSEPSTR, strlen(KEYSEPSTR));
          vP += strlen(KEYSEPSTR);
          memcpy(vP, token, tsiz);
          tsiz = strlen(KEYSEPSTR)+tsiz;
        }
        else {
          GET_INDEX_KEY_NAME(vIdxName, mcond->ColumnName, mcond->ColumnNameSize, token, tsiz);
        }
        tcbdbcurjump(cur, vIdxName, tsiz);
        const char *kbuf;
        int ksiz;
        while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
          //if(trim) ksiz -= 3;
          if (vIsPKIndex && ksiz != esiz) continue;
          if(ksiz > tsiz && !memcmp(kbuf, vIdxName, tsiz)){
            int vsiz;
            const char *vbuf = tcbdbcurval3(cur, &vsiz);
            int nsiz;
            if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){
              if(acnum < 1){
                TCLISTPUSH(res, vbuf, vsiz);
              } else if(ucond){
                if(tcDBQueryOneCondMatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz);
              } else if(tcDBQueryAllCondMatch(qry, vbuf, vsiz)){
                TCLISTPUSH(res, vbuf, vsiz);
              }
            }
          } else {
            break;
          }
          tcbdbcurnext(cur);
        }
      }
      tclistdel(tokens);
      tcbdbcurdel(cur);
    } else if(mcond->op == TDBQCNUMEQ){
      tcxstrprintf(hint, "using an index: \"%s\" asc (NUMEQ)\n", mcond->ColumnName);
#if defined(DEBUG)
      fprintf(stderr, "using an index: \"%s\" asc (NUMEQ)\n", mcond->ColumnName);
#endif
      BDBCUR *cur;
      if (vIsPKIndex) {
        cur = tcbdbcurnew(vDB->DB);
      }
      else {
        cur = tcbdbcurnew(vDB->IndexDB);
      }
      if(oname && !strcmp(oname, mcond->ColumnName)) oname = NULL;
      long double xnum = tcDBatof(expr);
      tcDBQueryIdxCurJumpNum(cur, mcond->ColumnName, mcond->ColumnNameSize, expr, esiz, true);
      bool all = oname != NULL;
      if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max);
      const char *kbuf;
      int ksiz;
      while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
        if(tcDBatof(GetFieldValueFromIndexKey(kbuf, mcond->ColumnNameSize)) == xnum){
          int vsiz;
          const char *vbuf = tcbdbcurval3(cur, &vsiz);
          int nsiz;
          if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){
            if(acnum < 1){
              TCLISTPUSH(res, vbuf, vsiz);
            } else if(ucond){
              if(tcDBQueryOneCondMatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz);
            } else if(tcDBQueryAllCondMatch(qry, vbuf, vsiz)){
              TCLISTPUSH(res, vbuf, vsiz);
            }
          }
        } else {
          break;
        }
        tcbdbcurnext(cur);
      }
      tcbdbcurdel(cur);
    } else if(mcond->op == TDBQCNUMGT || mcond->op == TDBQCNUMGE){
      if(oname && !strcmp(oname, mcond->ColumnName) && otype == TDBQONUMDESC){
        tcxstrprintf(hint, "using an index: \"%s\" desc (NUMGT/NUMGE)\n", mcond->ColumnName);
#if defined(DEBUG)
        fprintf(stderr, "using an index: \"%s\" desc (NUMGT/NUMGE)\n", mcond->ColumnName);
#endif
        long double xnum = tcDBatof(expr);
        BDBCUR *cur;
        DBCURJUMPBACKBW(cur, mcond->ColumnName, mcond->ColumnNameSize, vIsPKIndex);
        //tcbdbcurlast(cur);
        if(max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max);
        const char *kbuf;
        int ksiz;
        while(TCLISTNUM(res) < max && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
          long double knum = tcDBatof(GetFieldValueFromIndexKey(kbuf, mcond->ColumnNameSize));
          if(knum < xnum) break;
          if(knum > xnum || (knum >= xnum && mcond->op == TDBQCNUMGE)){
            int vsiz;
            const char *vbuf = tcbdbcurval3(cur, &vsiz);
            int nsiz;
            if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){
              if(acnum < 1){
                TCLISTPUSH(res, vbuf, vsiz);
              } else if(ucond){
                if(tcDBQueryOneCondMatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz);
              } else if(tcDBQueryAllCondMatch(qry, vbuf, vsiz)){
                TCLISTPUSH(res, vbuf, vsiz);
              }
            }
          }
          tcbdbcurprev(cur);
        }
        tcbdbcurdel(cur);
        oname = NULL;
      } else {
        tcxstrprintf(hint, "using an index: \"%s\" asc (NUMGT/NUMGE)\n", mcond->ColumnName);
#if defined(DEBUG)
        fprintf(stderr, "using an index: \"%s\" asc (NUMGT/NUMGE)\n", mcond->ColumnName);
#endif
        long double xnum = tcDBatof(expr);
        BDBCUR *cur = tcbdbcurnew(vDB->IndexDB);
        tcDBQueryIdxCurJumpNum(cur, mcond->ColumnName, mcond->ColumnNameSize, expr, esiz, true);
        bool all = oname && (strcmp(oname, mcond->ColumnName) || otype != TDBQONUMASC);
        if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max);
        const char *kbuf;
        int ksiz;
        while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
          long double knum = tcDBatof(GetFieldValueFromIndexKey(kbuf, mcond->ColumnNameSize));
          if(knum > xnum || (knum >= xnum && mcond->op == TDBQCNUMGE)){
            int vsiz;
            const char *vbuf = tcbdbcurval3(cur, &vsiz);
            int nsiz;
            if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){
              if(acnum < 1){
                TCLISTPUSH(res, vbuf, vsiz);
              } else if(ucond){
                if(tcDBQueryOneCondMatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz);
              } else if(tcDBQueryAllCondMatch(qry, vbuf, vsiz)){
                TCLISTPUSH(res, vbuf, vsiz);
              }
            }
          }
          tcbdbcurnext(cur);
        }
        tcbdbcurdel(cur);
        if(!all) oname = NULL;
      }
    } else if(mcond->op == TDBQCNUMLT || mcond->op == TDBQCNUMLE){
      if(oname && !strcmp(oname, mcond->ColumnName) && otype == TDBQONUMASC){
        tcxstrprintf(hint, "using an index: \"%s\" asc (NUMLT/NUMLE)\n", mcond->ColumnName);
#if defined(DEBUG)
        fprintf(stderr, "using an index: \"%s\" asc (NUMLT/NUMLE)\n", mcond->ColumnName);
#endif
        long double xnum = tcDBatof(expr);
        BDBCUR *cur;// = tcbdbcurnew(vDB->IndexDB);
        DBCURJUMPBW(cur, mcond->ColumnName, mcond->ColumnNameSize, vIsPKIndex);
        if(max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max);
        const char *kbuf;
        int ksiz;
        while(TCLISTNUM(res) < max && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
          long double knum = tcDBatof(GetFieldValueFromIndexKey(kbuf, mcond->ColumnNameSize));
          if(knum > xnum) break;
          if(knum < xnum || (knum <= xnum && mcond->op == TDBQCNUMLE)){
            int vsiz;
            const char *vbuf = tcbdbcurval3(cur, &vsiz);
            int nsiz;
            if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){
              if(acnum < 1){
                TCLISTPUSH(res, vbuf, vsiz);
              } else if(ucond){
                if(tcDBQueryOneCondMatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz);
              } else if(tcDBQueryAllCondMatch(qry, vbuf, vsiz)){
                TCLISTPUSH(res, vbuf, vsiz);
              }
            }
          }
          tcbdbcurnext(cur);
        }
        tcbdbcurdel(cur);
        oname = NULL;
      } else {
        tcxstrprintf(hint, "using an index: \"%s\" desc (NUMLT/NUMLE)\n", mcond->ColumnName);
#if defined(DEBUG)
        fprintf(stderr, "using an index: \"%s\" desc (NUMLT/NUMLE)\n", mcond->ColumnName);
#endif
        long double xnum = tcDBatof(expr);
        BDBCUR *cur = tcbdbcurnew(vDB->IndexDB);
        tcDBQueryIdxCurJumpNum(cur, mcond->ColumnName,  mcond->ColumnNameSize, expr, esiz, false);
        bool all = oname && (strcmp(oname, mcond->ColumnName) || otype != TDBQONUMDESC);
        if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max);
        const char *kbuf;
        int ksiz;
        while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
          long double knum = tcDBatof(GetFieldValueFromIndexKey(kbuf, mcond->ColumnNameSize));
          if(knum < xnum || (knum <= xnum && mcond->op == TDBQCNUMLE)){
            int vsiz;
            const char *vbuf = tcbdbcurval3(cur, &vsiz);
            int nsiz;
            if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){
              if(acnum < 1){
                TCLISTPUSH(res, vbuf, vsiz);
              } else if(ucond){
                if(tcDBQueryOneCondMatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz);
              } else if(tcDBQueryAllCondMatch(qry, vbuf, vsiz)){
                TCLISTPUSH(res, vbuf, vsiz);
              }
            }
          }
          tcbdbcurprev(cur);
        }
        tcbdbcurdel(cur);
        if(!all) oname = NULL;
      }
    } else if(mcond->op == TDBQCNUMBT){
      tcxstrprintf(hint, "using an index: \"%s\" asc (NUMBT)\n", mcond->ColumnName);
#if defined(DEBUG)
      fprintf(stderr, "using an index: \"%s\" asc (NUMBT)\n", mcond->ColumnName);
#endif
      while(*expr == ' ' || *expr == ','){
        expr++;
      }
      const char *pv = expr;
      while(*pv != '\0' && *pv != ' ' && *pv != ','){
        pv++;
      }
      esiz = pv - expr;
      if(*pv != ' ' && *pv != ',') pv = " ";
      pv++;
      while(*pv == ' ' || *pv == ','){
        pv++;
      }
      long double lower = tcDBatof(expr);
      long double upper = tcDBatof(pv);
      if(lower > upper){
        expr = pv;
        esiz = strlen(expr);
        long double swap = lower;
        lower = upper;
        upper = swap;
      }
      BDBCUR *cur = tcbdbcurnew(vDB->IndexDB);
      tcDBQueryIdxCurJumpNum(cur, mcond->ColumnName, mcond->ColumnNameSize, expr, esiz, true);
      bool all = oname && (strcmp(oname, mcond->ColumnName) || otype != TDBQONUMASC);
      if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max);
      const char *kbuf;
      int ksiz;
      while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
        if(tcDBatof(GetFieldValueFromIndexKey(kbuf, mcond->ColumnNameSize)) > upper) break;
        int vsiz;
        const char *vbuf = tcbdbcurval3(cur, &vsiz);
        int nsiz;
        if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){
          if(acnum < 1){
            TCLISTPUSH(res, vbuf, vsiz);
          } else if(ucond){
            if(tcDBQueryOneCondMatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz);
          } else if(tcDBQueryAllCondMatch(qry, vbuf, vsiz)){
            TCLISTPUSH(res, vbuf, vsiz);
          }
        }
        tcbdbcurnext(cur);
      }
      tcbdbcurdel(cur);
      if(oname && !strcmp(oname, mcond->ColumnName)){
        if(otype == TDBQONUMASC){
          oname = NULL;
        } else if(otype == TDBQONUMDESC){
          tclistinvert(res);
          oname = NULL;
        }
      }
    } else if(mcond->op == TDBQCNUMOREQ){
      tcxstrprintf(hint, "using an index: \"%s\" skip (NUMOREQ)\n", mcond->ColumnName);
#if defined(DEBUG)
      fprintf(stderr, "using an index: \"%s\" skip (NUMOREQ)\n", mcond->ColumnName);
#endif
      BDBCUR *cur = tcbdbcurnew(vDB->IndexDB);
      TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,");
      tclistsortex(tokens, tcDBCmpPkeyNumAsc);
      for(int i = 1; i < TCLISTNUM(tokens); i++){
        if(tcDBatof(TCLISTVALPTR(tokens, i)) == tcDBatof(TCLISTVALPTR(tokens, i - 1))){
          TCFREE(tclistremove2(tokens, i));
          i--;
        }
      }
      if(oname && !strcmp(oname, mcond->ColumnName)){
        if(otype == TDBQONUMASC){
          oname = NULL;
        } else if(otype == TDBQONUMDESC){
          tclistinvert(tokens);
          oname = NULL;
        }
      }
      int tnum = TCLISTNUM(tokens);
      bool all = oname != NULL;
      if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max);
      for(int i = 0; (all || TCLISTNUM(res) < max) && i < tnum; i++){
        const char *token;
        int tsiz;
        TCLISTVAL(token, tokens, i, tsiz);
        if(tsiz < 1) continue;
        long double xnum = tcDBatof(token);
        tcDBQueryIdxCurJumpNum(cur, mcond->ColumnName, mcond->ColumnNameSize, token, tsiz, true);
        const char *kbuf;
        int ksiz;
        while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
          if(tcDBatof(GetFieldValueFromIndexKey(kbuf, mcond->ColumnNameSize)) == xnum){
            int vsiz;
            const char *vbuf = tcbdbcurval3(cur, &vsiz);
            int nsiz;
            if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){
              if(acnum < 1){
                TCLISTPUSH(res, vbuf, vsiz);
              } else if(ucond){
                if(tcDBQueryOneCondMatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz);
              } else if(tcDBQueryAllCondMatch(qry, vbuf, vsiz)){
                TCLISTPUSH(res, vbuf, vsiz);
              }
            }
          } else {
            break;
          }
          tcbdbcurnext(cur);
        }
      }
      tclistdel(tokens);
      tcbdbcurdel(cur);
    }
    if(nmap) tcmapdel(nmap);
  }
  if(!res && scond){
    res = tclistnew();
    scond->alive = false;
    acnum--;
    TDBQueryCondition *ucond = NULL;
    for(int i = 0; i < cnum; i++){
      TDBQueryCondition *cond = conds + i;
      if(!cond->alive) continue;
      if(ucond){
        ucond = NULL;
        break;
      }
      ucond = cond;
    }
    //bool trim = *sidx->name != '\0';
    bool vIsPKIndex = *scond->ColumnName == '\0';
    bool asc = true;
    bool all = true;
    if(!oname){
      all = false;
    } else if(!strcmp(oname, scond->ColumnName)){
      switch(scond->IndexType){
        case TDBITLEXICAL:
          switch(otype){
            case TDBQOSTRASC:
              asc = true;
              all = false;
              break;
            case TDBQOSTRDESC:
              asc = false;
              all = false;
              break;
          }
          break;
        case TDBITDECIMAL:
          switch(otype){
            case TDBQONUMASC:
              asc = true;
              all = false;
              break;
            case TDBQONUMDESC:
              asc = false;
              all = false;
              break;
          }
          break;
      }
    }
    if(asc){
      tcxstrprintf(hint, "using an index: \"%s\" asc (all)\n", scond->ColumnName);
#if defined(DEBUG)
      fprintf(stderr, "using an index: \"%s\" asc (all)\n", scond->ColumnName);
#endif
      BDBCUR *cur;// = tcbdbcurnew(vDB->IndexDB);
      DBCURJUMPBW(cur, scond->ColumnName, scond->ColumnNameSize, vIsPKIndex);
      if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max);
      const char *kbuf;
      int ksiz;
      while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
        //if(trim) ksiz -= 3;
        if(ksiz < 0) break;
        char *vValue = GetFieldValueFromIndexKey(kbuf, scond->ColumnNameSize);
        if(tcDBQueryCondMatch(scond, vValue, strlen(vValue))){
          int vsiz;
          const char *vbuf = tcbdbcurval3(cur, &vsiz);
          if(acnum < 1){
            TCLISTPUSH(res, vbuf, vsiz);
          } else if(ucond){
            if(tcDBQueryOneCondMatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz);
          } else if(tcDBQueryAllCondMatch(qry, vbuf, vsiz)){
            TCLISTPUSH(res, vbuf, vsiz);
          }
        }
        tcbdbcurnext(cur);
      }
      tcbdbcurdel(cur);
    } else {
      tcxstrprintf(hint, "using an index: \"%s\" desc (all)\n", scond->ColumnName);
#if defined(DEBUG)
      fprintf(stderr, "using an index: \"%s\" desc (all)\n", scond->ColumnName);
#endif
      BDBCUR *cur;// = tcbdbcurnew(vDB->IndexDB);
      DBCURJUMPBACKBW(cur, scond->ColumnName, scond->ColumnNameSize, vIsPKIndex);
      if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max);
      const char *kbuf;
      int ksiz;
      while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
        //if(trim) ksiz -= 3;
        if(ksiz < 0) break;
        char *vValue = GetFieldValueFromIndexKey(kbuf, scond->ColumnNameSize);
        if(tcDBQueryCondMatch(scond, vValue, strlen(vValue))){
          int vsiz;
          const char *vbuf = tcbdbcurval3(cur, &vsiz);
          if(acnum < 1){
            TCLISTPUSH(res, vbuf, vsiz);
          } else if(ucond){
            if(tcDBQueryOneCondMatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz);
          } else if(tcDBQueryAllCondMatch(qry, vbuf, vsiz)){
            TCLISTPUSH(res, vbuf, vsiz);
          }
        }
        tcbdbcurprev(cur);
      }
      tcbdbcurdel(cur);
    }
    if(!all) oname = NULL;
  }
  if(!res && oname && max < tcDBGetRecordNumImpl(vDB) * TDBORDRATIO){
    //TDBIDX *oidx = NULL;
    bool asc = true;
    int onamesize = strlen(oname);
    int vIndexType = tcDBGetFieldIndexType(vDB, oname, onamesize);
    switch(vIndexType){
      case TDBITLEXICAL:
        switch(otype){
          case TDBQOSTRASC:
            asc = true;
            break;
          case TDBQOSTRDESC:
            asc = false;
            break;
        }
        break;
      case TDBITDECIMAL:
        switch(otype){
          case TDBQONUMASC:
            asc = true;
            break;
          case TDBQONUMDESC:
            asc = false;
            break;
        }
        break;
    }
    if(vIndexType != TDBITVOID){
      res = tclistnew();
      TDBQueryCondition *ucond = NULL;
      for(int i = 0; i < cnum; i++){
        TDBQueryCondition *cond = conds + i;
        if(!cond->alive) continue;
        if(ucond){
          ucond = NULL;
          break;
        }
        ucond = cond;
      }
      //bool trim = *oidx->name != '\0';
      bool vIsPKIndex = *oname == '\0';
      if(asc){
        tcxstrprintf(hint, "using an index: \"%s\" asc (order)\n", oname);
#if defined(DEBUG)
        fprintf(stderr, "using an index: \"%s\" asc (order)\n", oname);
#endif
        BDBCUR *cur;// = tcbdbcurnew(vDB->IndexDB);
        DBCURJUMPBW(cur, oname, onamesize, vIsPKIndex);
        tcxstrprintf(hint, "limited matching: %d\n", max);
        const char *kbuf;
        int ksiz;
        while(TCLISTNUM(res) < max && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
          //if(trim) ksiz -= 3;
          if(ksiz < 0) break;
          int vsiz;
          const char *vbuf = tcbdbcurval3(cur, &vsiz);
          if(acnum < 1){
            TCLISTPUSH(res, vbuf, vsiz);
          } else if(ucond){
            if(tcDBQueryOneCondMatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz);
          } else if(tcDBQueryAllCondMatch(qry, vbuf, vsiz)){
            TCLISTPUSH(res, vbuf, vsiz);
          }
          tcbdbcurnext(cur);
        }
        tcbdbcurdel(cur);
      } else {
        tcxstrprintf(hint, "using an index: \"%s\" desc (order)\n", oname);
#if defined(DEBUG)
        fprintf(stderr, "using an index: \"%s\" desc (order)\n", oname);
#endif
        BDBCUR *cur;// = tcbdbcurnew(vDB->IndexDB);
        DBCURJUMPBACKBW(cur, oname, onamesize, vIsPKIndex);
        tcxstrprintf(hint, "limited matching: %d\n", max);
        const char *kbuf;
        int ksiz;
        while(TCLISTNUM(res) < max && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){
          //if(trim) ksiz -= 3;
          //if(ksiz < 0) break;
          int vsiz;
          const char *vbuf = tcbdbcurval3(cur, &vsiz);
          if(acnum < 1){
            TCLISTPUSH(res, vbuf, vsiz);
          } else if(ucond){
            if(tcDBQueryOneCondMatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz);
          } else if(tcDBQueryAllCondMatch(qry, vbuf, vsiz)){
            TCLISTPUSH(res, vbuf, vsiz);
          }
          tcbdbcurprev(cur);
        }
        tcbdbcurdel(cur);
      }
      int rnum = TCLISTNUM(res);
      if(rnum >= max || tcDBGetFieldIndexCountImpl(vDB, oname, onamesize) >= tcDBGetRecordNumImpl(vDB)){
        oname = NULL;
      } else {
        tcxstrprintf(hint, "abort the result: %d\n", rnum);
#if defined(DEBUG)
        fprintf(stderr, "abort the result: %d\n", rnum);
#endif
        tclistdel(res);
        res = NULL;
      }
    }
  }
  if(!res){
    tcxstrprintf(hint, "scanning the whole table\n");
#if defined(DEBUG)
    fprintf(stderr, "scanning the whole table\n");
#endif
    res = tclistnew();
    TDBQueryCondition *ucond = NULL;
    for(int i = 0; i < cnum; i++){  //whether only one condition left?
      TDBQueryCondition *cond = conds + i;
      if(!cond->alive) continue;
      if(ucond){
        ucond = NULL;
        break;
      }
      ucond = cond;
    }
    //char *lkbuf = NULL;
    //int lksiz = 0;
    char *pkbuf;
    int pksiz;
    const char *cbuf;
    int csiz;
    bool all = oname != NULL;
    if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max);
    BDBCUR *cur = tcbdbcurnew(vDB->DB);
    tcbdbcurjump(cur, KEYSEPSTR, strlen(KEYSEPSTR));
    while((all || TCLISTNUM(res) < max) &&
          (pkbuf = tcbdbcurkey3(cur, &pksiz)) != NULL){
      if ((pksiz <= 1) || (*pkbuf != KEYSEPCHAR)) break;
      pkbuf++;
      pksiz--;
      if(ucond){
        int vsiz;
        char *vbuf;
        bool vIsPKIndex = *ucond->ColumnName == '\0';
        if (vIsPKIndex) {
          vbuf = pkbuf;
          vsiz = pksiz;
        }
        else {
          vbuf = tcDBGetFieldValueStr(vDB, pkbuf, pksiz, ucond->ColumnName, ucond->ColumnNameSize, &vsiz);
        }

        if(vbuf){
          //char *vValue = GetFieldValueFromIndexKey(vbuf, ucond->ColumnNameSize);
          //printf("value=%s\n", vbuf);
          if(tcDBQueryCondMatch(ucond, vbuf, vsiz) == ucond->sign)
            TCLISTPUSH(res, pkbuf, pksiz);
          if (!vIsPKIndex) TCFREE(vbuf);
        } else {
          if(!ucond->sign) TCLISTPUSH(res, pkbuf, pksiz);
        }
      } else {
        cbuf = tcbdbcurval3(cur, &csiz);
        TCMAP *cols = tcDBGetColumnValuesMapFromStr(vDB, pkbuf, pksiz, cbuf, csiz);
        bool ok = true;
        for(int i = 0; i < cnum; i++){
          TDBQueryCondition *cond = conds + i;
          int vsiz;
          const char *vbuf;
          bool vIsPKIndex = *cond->ColumnName == '\0';
          if (vIsPKIndex) {
            vbuf = pkbuf;
            vsiz = pksiz;
          }
          else {
            vbuf = tcmapget(cols, cond->ColumnName, cond->ColumnNameSize, &vsiz);
          }
            if(vbuf){
              //char *vValue = GetFieldValueFromIndexKey(vbuf, cond->ColumnNameSize);
              //printf("%s=%s %d=%d\n", cond->ColumnName, vbuf, cond->op, TDBQCNUMGE);
              if(tcDBQueryCondMatch(cond, vbuf, vsiz) != cond->sign){
                ok = false;
                break;
              }
            } else {
              if(cond->sign){
                ok = false;
                break;
              }
            }
        }
        if(ok) TCLISTPUSH(res, pkbuf, pksiz);
        tcmapdel(cols);
      }
      //TCFREE(lkbuf);
      tcbdbcurnext(cur);
      //lkbuf = pkbuf;
      //lksiz = pksiz;
    }
    //TCFREE(lkbuf);
    tcbdbcurdel(cur);
  }
  int rnum = TCLISTNUM(res);
  tcxstrprintf(hint, "result set size: %d\n", rnum);
  if(oname){
    if(*oname == '\0'){
      tcxstrprintf(hint, "sorting the result set: \"%s\"\n", oname);
      switch(otype){
        case TDBQOSTRASC:
          tclistsort(res);
          break;
        case TDBQOSTRDESC:
          tclistsort(res);
          tclistinvert(res);
          break;
        case TDBQONUMASC:
          tclistsortex(res, tcDBCmpPkeyNumAsc);
          break;
        case TDBQONUMDESC:
          tclistsortex(res, tcDBCmpPkeyNumDesc);
          break;
      }
    } else {
      tcxstrprintf(hint, "sorting the result set: \"%s\"\n", oname);
      TDBSORTREC *keys;
      TCMALLOC(keys, sizeof(*keys) * rnum + 1);
      int onsiz = strlen(oname);
      for(int i = 0; i < rnum; i++){
        TDBSORTREC *key = keys + i;
        const char *kbuf;
        int ksiz;
        TCLISTVAL(kbuf, res, i, ksiz);
        char *vbuf = NULL;
        int vsiz = 0;
        vbuf = tcDBGetFieldValueStr(vDB, kbuf, ksiz, oname, onsiz, &vsiz);
        key->kbuf = kbuf;
        key->ksiz = ksiz;
        key->vbuf = vbuf;
        key->vsiz = vsiz;
      }
      int (*compar)(const TDBSORTREC *a, const TDBSORTREC *b) = NULL;
      switch(otype){
        case TDBQOSTRASC:
          compar = tcDBCmpSortRecStrAsc;
          break;
        case TDBQOSTRDESC:
          compar = tcDBCmpSortRecStrDesc;
          break;
        case TDBQONUMASC:
          compar = tcDBCmpSortRecNumAsc;
          break;
        case TDBQONUMDESC:
          compar = tcDBCmpSortRecNumDesc;
          break;
      }
      if(compar){
        if(max <= rnum / 16){
          tctopsort(keys, rnum, sizeof(*keys), max, (int (*)(const void *, const void *))compar);
        } else {
          qsort(keys, rnum, sizeof(*keys), (int (*)(const void *, const void *))compar);
        }
      }
      TCLIST *nres = tclistnew2(rnum);
      for(int i = 0; i < rnum; i++){
        TDBSORTREC *key = keys + i;
        TCLISTPUSH(nres, key->kbuf, key->ksiz);
        TCFREE(key->vbuf);
      }
      tclistdel(res);
      res = nres;
      TCFREE(keys);
    }
  } else if(isord){
    tcxstrprintf(hint, "leaving the index order\n");
  } else {
    tcxstrprintf(hint, "leaving the natural order\n");
  }
  if(qry->skip > 0){
    int left = tclmin(TCLISTNUM(res), qry->skip);
    while(left-- > 0){
      int rsiz;
      TCFREE(tclistshift(res, &rsiz));
    }
    max -= qry->skip;
  }
  if(TCLISTNUM(res) > max){
    int left = TCLISTNUM(res) - max;
    while(left-- > 0){
      int rsiz;
      TCFREE(tclistpop(res, &rsiz));
    }
  }
  qry->count = TCLISTNUM(res);
  return res;
}

/* Execute the search of a query object. */
TCLIST *tcDBQuerySearch(TDBQuery *qry){
  assert(qry);
  TBTreeDatabase *vDB = qry->DB;
  if(!DBLOCKMETHOD(vDB, false)) return tclistnew();
  if(!vDB->Opened){
    tcDBRaiseError(vDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(vDB);
    return tclistnew();
  }
  TCLIST *vResult = tcDBQuerySearchImpl(qry);
  DBUNLOCKMETHOD(vDB);
  return vResult;
}

/* Answer to remove for each record of a query.
 `pkbuf' is ignored.
 `pksiz' is ignored.
 `op' is ignored.
 The return value is always `TDBQPOUT'. */
static int tcDBQueryProcDelete(const void *pkbuf, int pksiz, TCMAP *cols, void *op){
  assert(pkbuf && pksiz >= 0 && cols);
  return TDBQPOUT;
}

/* Remove each record corresponding to a query object. */
bool tcDBQuerySearchDelete(TDBQuery *qry){
  assert(qry);
  return tcDBQueryProc(qry, tcDBQueryProcDelete, NULL);
}

/* Process each record corresponding to a query object. */
bool tcDBQueryProc(TDBQuery *qry, TDBQRYPROC proc, void *op){
  ///*
  assert(qry && proc);
  TBTreeDatabase *vDB = qry->DB;
  if(!DBLOCKMETHOD(vDB, true)) return false;
  if(!vDB->Opened || !vDB->Writable){
    tcDBRaiseError(vDB, TCEINVALID, __FILE__, __LINE__, __func__);
    DBUNLOCKMETHOD(vDB);
    return false;
  }
  bool err = false;
  int64_t getnum = 0;
  int64_t putnum = 0;
  int64_t outnum = 0;
  TCLIST *res = tcDBQuerySearchImpl(qry);
  int rnum = TCLISTNUM(res);
  for(int i = 0; i < rnum; i++){
    const char *pkbuf;
    int pksiz;
    TCLISTVAL(pkbuf, res, i, pksiz);
    TCMAP *cols = tcDBGetColumnValuesMapImpl(vDB, pkbuf, pksiz);
    if(!cols){
      err = true;
      continue;
    }
    getnum++;
    int flags = proc(pkbuf, pksiz, cols, op);
    if(flags & TDBQPPUT){
      if(tcDBPutColumnsImpl(vDB, pkbuf, pksiz, cols, TDBPDOVER)){
        putnum++;
      } else {
        err = true;
      }
    } else if(flags & TDBQPOUT){
      if(tcDBDeleteImpl(vDB, pkbuf, pksiz, tcbdbout)){
        outnum++;
      } else {
        err = true;
      }
    }
    tcmapdel(cols);
    if(flags & TDBQPSTOP) break;
  }
  tclistdel(res);
  tcxstrprintf(qry->hint, "post treatment: get=%lld, put=%lld, out=%lld\n",
               (long long)getnum, (long long)putnum, (long long)outnum);
  DBUNLOCKMETHOD(vDB);
  return !err;
   //*/
}


/* Get the hint string of a query object. */
const char *tcDBQueryHint(TDBQuery *qry){
  assert(qry);
  return tcxstrptr(qry->hint);
}


/* Retrieve records with multiple query objects and get the set of the result. */
TCLIST *tcDBMetaSearch(TDBQuery **qrys, int num, int type){
  assert(qrys && num >= 0);
  if(num < 1) return tclistnew2(1);
  if(num < 2) return tcDBQuerySearch(qrys[0]);
  const char *oname = qrys[0]->OrderColumnName;
  int onsiz = oname ? strlen(oname) : 0;
  int OrderType = qrys[0]->OrderType;
  TCMAP *uset = tcmapnew();
  if(type == TDBMSUNION){
    for(int i = 0; i < num; i++){
      TDBQuery *qry = qrys[i];
      TBTreeDatabase *tdb = qry->DB;
      int omax = qry->max;
      int oskip = qry->skip;
      if(qry->max < INT_MAX - qry->skip) qry->max += qry->skip;
      qry->skip = 0;
      TCLIST *res = tcDBQuerySearch(qry);
      qry->max = omax;
      qry->skip = oskip;
      int rnum = TCLISTNUM(res);
      for(int j = 0; j < rnum; j++){
        const char *pkbuf;
        int pksiz;
        TCLISTVAL(pkbuf, res, j, pksiz);
        if(oname){
          int csiz;
          char *cbuf = tcDBGetFieldValue(tdb, pkbuf, pksiz, oname, onsiz, &csiz);
          if(cbuf){
            tcmapput4(uset, pkbuf, pksiz, "=", 1, cbuf, csiz);
            TCFREE(cbuf);
          } else {
            tcmapput(uset, pkbuf, pksiz, "*", 1);
          }
        } else {
          tcmapputkeep(uset, pkbuf, pksiz, "", 0);
        }
      }
      tclistdel(res);
    }
  } else if(type == TDBMSISECT){
    int omax = qrys[0]->max;
    int oskip = qrys[0]->skip;
    qrys[0]->max = INT_MAX;
    qrys[0]->skip = 0;
    TCLIST *res = tcDBQuerySearch(qrys[0]);
    qrys[0]->max = omax;
    qrys[0]->skip = oskip;
    int rnum = TCLISTNUM(res);
    for(int i = 0; i < rnum; i++){
      const char *pkbuf;
      int pksiz;
      TCLISTVAL(pkbuf, res, i, pksiz);
      tcmapputkeep(uset, pkbuf, pksiz, "", 0);
    }
    tclistdel(res);
    for(int i = 1; i < num; i++){
      TDBQuery *qry = qrys[i];
      if(TCMAPRNUM(uset) < 1){
        tcxstrclear(qry->hint);
        tcxstrprintf(qry->hint, "omitted\n");
        continue;
      }
      omax = qry->max;
      oskip = qry->skip;
      qry->max = INT_MAX;
      qry->skip = 0;
      res = tcDBQuerySearch(qry);
      qry->max = omax;
      qry->skip = oskip;
      rnum = TCLISTNUM(res);
      TCMAP *nset = tcmapnew2(tclmin(TCMAPRNUM(uset), rnum) + 1);
      for(int j = 0; j < rnum; j++){
        const char *pkbuf;
        int pksiz;
        TCLISTVAL(pkbuf, res, j, pksiz);
        int vsiz;
        if(tcmapget(uset, pkbuf, pksiz, &vsiz)) tcmapputkeep(nset, pkbuf, pksiz, "", 0);
      }
      tcmapdel(uset);
      uset = nset;
      tclistdel(res);
    }
  } else if(type == TDBMSDIFF){
    int omax = qrys[0]->max;
    int oskip = qrys[0]->skip;
    qrys[0]->max = INT_MAX;
    qrys[0]->skip = 0;
    TCLIST *res = tcDBQuerySearch(qrys[0]);
    qrys[0]->max = omax;
    qrys[0]->skip = oskip;
    int rnum = TCLISTNUM(res);
    for(int i = 0; i < rnum; i++){
      const char *pkbuf;
      int pksiz;
      TCLISTVAL(pkbuf, res, i, pksiz);
      tcmapputkeep(uset, pkbuf, pksiz, "", 0);
    }
    tclistdel(res);
    for(int i = 1; i < num; i++){
      TDBQuery *qry = qrys[i];
      if(TCMAPRNUM(uset) < 1){
        tcxstrclear(qry->hint);
        tcxstrprintf(qry->hint, "omitted\n");
        continue;
      }
      omax = qry->max;
      oskip = qry->skip;
      qry->max = INT_MAX;
      qry->skip = 0;
      res = tcDBQuerySearch(qry);
      qry->max = omax;
      qry->skip = oskip;
      rnum = TCLISTNUM(res);
      for(int j = 0; j < rnum; j++){
        const char *pkbuf;
        int pksiz;
        TCLISTVAL(pkbuf, res, j, pksiz);
        tcmapout(uset, pkbuf, pksiz);
      }
      tclistdel(res);
    }
  }
  int max = qrys[0]->max;
  int skip = qrys[0]->skip;
  TCLIST *res;
  if(oname && type == TDBMSUNION){
    int rnum = TCMAPRNUM(uset);
    TDBSORTREC *keys;
    TCMALLOC(keys, sizeof(*keys) * rnum + 1);
    tcmapiterinit(uset);
    const char *pkbuf;
    int pksiz;
    TDBSORTREC *key = keys;
    while((pkbuf = tcmapiternext(uset, &pksiz)) != NULL){
      int vsiz;
      const char *vbuf = tcmapiterval(pkbuf, &vsiz);
      key->kbuf = pkbuf;
      key->ksiz = pksiz;
      if(*vbuf == '='){
        key->vbuf = (char *)vbuf + 1;
        key->vsiz = vsiz - 1;
      } else {
        key->vbuf = NULL;
        key->vsiz = 0;
      }
      key++;
    }
    int (*compar)(const TDBSORTREC *a, const TDBSORTREC *b) = NULL;
    switch(OrderType){
      case TDBQOSTRASC:
        compar = tcDBCmpSortRecStrAsc;
        break;
      case TDBQOSTRDESC:
        compar = tcDBCmpSortRecStrDesc;
        break;
      case TDBQONUMASC:
        compar = tcDBCmpSortRecNumAsc;
        break;
      case TDBQONUMDESC:
        compar = tcDBCmpSortRecNumDesc;
        break;
    }
    if(compar) qsort(keys, rnum, sizeof(*keys), (int (*)(const void *, const void *))compar);
    res = tclistnew2(tclmin(rnum, max));
    for(int i = skip; max > 0 && i < rnum; i++){
      key = keys + i;
      TCLISTPUSH(res, key->kbuf, key->ksiz);
      max--;
    }
    TCFREE(keys);
  } else {
    res = tclistnew2(tclmin(tcmaprnum(uset), max));
    tcmapiterinit(uset);
    const char *pkbuf;
    int pksiz;
    while(max > 0 && (pkbuf = tcmapiternext(uset, &pksiz)) != NULL){
      if(skip > 0){
        skip--;
      } else {
        TCLISTPUSH(res, pkbuf, pksiz);
        max--;
      }
    }
  }
  tcmapdel(uset);
  TCXSTR *hint = tcxstrnew();
  for(int i = 0; i < num; i++){
    TDBQuery *qry = qrys[i];
    TCLIST *lines = tcstrsplit(tcDBQueryHint(qry), "\n");
    int lnum = TCLISTNUM(lines);
    for(int j = 0; j < lnum; j++){
      const char *line = TCLISTVALPTR(lines, j);
      if(*line != 0) tcxstrprintf(hint, "[%d] %s\n", i, line);
    }
    tclistdel(lines);
    tcxstrclear(qry->hint);
    qry->count = 0;
  }
  TCXSTRCAT(qrys[0]->hint, TCXSTRPTR(hint), TCXSTRSIZE(hint));
  qrys[0]->count = TCLISTNUM(res);
  tcxstrdel(hint);
  return res;
//*/                                                            
}

/* Call a versatile function for miscellaneous operations of a database object.
 `aDB' specifies the multiple database object.
 `name' specifies the name of the function.
 `args' specifies a list object containing arguments.
 If successful, the return value is a list object of the result.
 */
TCLIST *tcDBExecCmd(void *aDB, const char *name, const TCLIST *args){
  assert(aDB && name && args);
  int argc = TCLISTNUM(args);
#if defined(DEBUG)
  fprintf(stderr, "DBMisc(%s) argc=%d\n", name, argc);
  for(int i = 0; i < argc; i++){
    const char *vbuf;
    int vsiz;
    TCLISTVAL(vbuf, args, i, vsiz);
    fprintf(stderr, "  args[%d]: %s\n", i, vbuf);
  }
#endif
  TCLIST *rv = NULL;
  register TBTreeDatabase *vDB = aDB; //tcDBPutColumnsImpl 
  if(!strcmp(name, "put") || !strcmp(name, "putkeep") || !strcmp(name, "putcat")){
    if(argc > 1){
      if(argc > 0){
        rv = tclistnew2(1);
        char *pkbuf;
        int pksiz;
        TCLISTVAL(pkbuf, args, 0, pksiz);
        argc--;
        TCMAP *cols = tcmapnew2(argc);
        for(int i = 1; i < argc; i += 2){
          const char *kbuf, *vbuf;
          int ksiz, vsiz;
          TCLISTVAL(kbuf, args, i, ksiz);
          TCLISTVAL(vbuf, args, i + 1, vsiz);
          tcmapput(cols, kbuf, ksiz, vbuf, vsiz);
        }
        bool err = false;
        if(!strcmp(name, "put")){
          if(!tcDBPutColumnsImpl(vDB, pkbuf, pksiz, cols, TDBPDOVER)) err = true;
        } else if(!strcmp(name, "putkeep")){
          if(!tcDBPutColumnsImpl(vDB, pkbuf, pksiz, cols, TDBPDKEEP)) err = true;
        } else if(!strcmp(name, "putcat")){
          if(!tcDBPutColumnsImpl(vDB, pkbuf, pksiz, cols, TDBPDCAT)) err = true;
        }
        tcmapdel(cols);
        if(err){
          tclistdel(rv);
          rv = NULL;
        }
      } else {
        rv = NULL;
      }
    }
  } else if (!strcmp(name, "putdup") || !strcmp(name, "putdupback")) {
    if(argc > 1){
      rv = tclistnew2(1);
      const char *kbuf;
      int ksiz;
      TCLISTVAL(kbuf, args, 0, ksiz);
      const char *vbuf;
      int vsiz;
      TCLISTVAL(vbuf, args, 1, vsiz);
      bool err = false;
      if(!strcmp(name, "putdup")){
        err = !tcDBReplaceImpl(aDB, kbuf, ksiz, vbuf, vsiz, TDBPDDUP);
      } else if(!strcmp(name, "putdupback")){
        err = !tcDBReplaceImpl(aDB, kbuf, ksiz, vbuf, vsiz, TDBPDDUPB);
      }
      if(err){
        tclistdel(rv);
        rv = NULL;
#if defined(DEBUG)
        fprintf(stderr, "DBReplace(putdup) Error %s.\n", kbuf); //err = true;
#endif
      }
    } else {
      rv = NULL;
    }
  } else if(!strcmp(name, "out")){
    if(argc > 0){
      rv = tclistnew2(1);
      const char *kbuf;
      int ksiz;
      TCLISTVAL(kbuf, args, 0, ksiz);
      if(!tcDBDeleteImpl(aDB, kbuf, ksiz, tcbdbout)){
        tclistdel(rv);
        rv = NULL;
      }
    } else {
      rv = NULL;
    }
  } else if(!strcmp(name, "get")){
    if(argc > 0){
      rv = tclistnew2(1);
      char *pkbuf;
      int pksiz;
      TCLISTVAL(pkbuf, args, 0, pksiz);
      TCMAP *cols = tcDBGetColumnValuesMap(vDB, pkbuf, pksiz);
      if(cols){
        tcmapiterinit(cols);
        const char *kbuf;
        int ksiz;
        while((kbuf = tcmapiternext(cols, &ksiz)) != NULL){
          int vsiz;
          const char *vbuf = tcmapiterval(kbuf, &vsiz);
          TCLISTPUSH(rv, kbuf, ksiz);
          TCLISTPUSH(rv, vbuf, vsiz);
        }
        tcmapdel(cols);
      } else {
        tclistdel(rv);
        rv = NULL;
      }
    } else {
      rv = NULL;
    }
  } else if(!strcmp(name, "putlist")){
    rv = tclistnew2(1);
    bool err = false;
    argc--;
    for(int i = 0; i < argc; i += 2){
      const char *kbuf, *vbuf;
      int ksiz, vsiz;
      TCLISTVAL(kbuf, args, i, ksiz);
      TCLISTVAL(vbuf, args, i + 1, vsiz);
      if(!tcDBReplaceImpl(aDB, kbuf, ksiz, vbuf, vsiz, TDBPDDUP)){
        err = true;
        break;
      }
    }
    if(err){
      tclistdel(rv);
      rv = NULL;
    }
  } else if(!strcmp(name, "outlist")){
    rv = tclistnew2(1);
    bool err = false;
    for(int i = 0; i < argc; i++){
      const char *kbuf;
      int ksiz;
      TCLISTVAL(kbuf, args, i, ksiz);
      if(!tcDBDeleteImpl(vDB, kbuf, ksiz, tcbdbout3) && tcbdbecode(vDB->DB) != TCENOREC){ //todo: tcbdbout3 can delete duplication record.
        err = true;
        break;
      }
    }
    if(err){
      tclistdel(rv);
      rv = NULL;
    }
  } else if(!strcmp(name, "getlist")){
    rv = tclistnew2(argc * 2);
    bool err = false;
    for(int i = 0; i < argc; i++){
      const char *kbuf;
      int ksiz;
      TCLISTVAL(kbuf, args, i, ksiz);
      TCLIST *vals = tcbdbget4(vDB->DB, kbuf, ksiz);
      if(vals){
        int vnum = TCLISTNUM(vals);
        for(int j = 0; j < vnum; j++){
          TCLISTPUSH(rv, kbuf, ksiz);
          const char *vbuf;
          int vsiz;
          TCLISTVAL(vbuf, vals, j, vsiz);
          TCLISTPUSH(rv, vbuf, vsiz);
        }
        tclistdel(vals);
      } else if(tcbdbecode(vDB->DB) != TCENOREC){
        err = true;
      }
    }
    if(err){
      tclistdel(rv);
      rv = NULL;
    }
  } else if(!strcmp(name, "getpart")){
    if(argc > 0){
      const char *kbuf;
      int ksiz;
      TCLISTVAL(kbuf, args, 0, ksiz);
      int off = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
      if(off < 0) off = 0;
      if(off > INT_MAX / 2 - 1) off = INT_MAX - 1;
      int len = argc > 2 ? tcatoi(TCLISTVALPTR(args, 2)) : -1;
      if(len < 0 || len > INT_MAX / 2) len = INT_MAX / 2;
      int vsiz;
      char *vbuf = tcbdbget(vDB->DB, kbuf, ksiz, &vsiz);
      if(vbuf){
        if(off < vsiz){
          rv = tclistnew2(1);
          vsiz -= off;
          if(vsiz > len) vsiz = len;
          if(off > 0) memmove(vbuf, vbuf + off, vsiz);
          tclistpushmalloc(rv, vbuf, vsiz);
        } else {
          rv = NULL;
          TCFREE(vbuf);
        }
      } else {
        rv = NULL;
      }
    } else {
      rv = NULL;
    }
  } else if(!strcmp(name, "iterinit")){
    rv = tclistnew2(1);
    bool err = false;
    if(argc > 0){
      const char *kbuf;
      int ksiz;
      TCLISTVAL(kbuf, args, 0, ksiz);
      if(!tcbdbcurjump(vDB->Cursor, kbuf, ksiz)) err = true;
    } else {
      if(!tcbdbcurfirst(vDB->Cursor)) err = true;
    }
    if(err){
      tclistdel(rv);
      rv = NULL;
    }
  } else if(!strcmp(name, "iternext")){
    rv = tclistnew2(1);
    int ksiz;
    const char *kbuf = tcbdbcurkey3(vDB->Cursor, &ksiz);
    if(kbuf){
      TCLISTPUSH(rv, kbuf, ksiz);
      int vsiz;
      const char *vbuf = tcbdbcurval3(vDB->Cursor, &vsiz);
      if(vbuf) TCLISTPUSH(rv, vbuf, vsiz);
      tcbdbcurnext(vDB->Cursor);
    } else {
      tclistdel(rv);
      rv = NULL;
    }
  } else if(!strcmp(name, "setindex")){
#if defined(DEBUG)
            fprintf(stderr, "%s\n", "setindex");
#endif
    rv = tclistnew2(1);
    bool err = false;
    argc--;
    for(int i = 0; i < argc; i += 2){
      const char *kbuf, *vbuf;
      int ksiz, vsiz;
      TCLISTVAL(kbuf, args, i, ksiz);
      TCLISTVAL(vbuf, args, i + 1, vsiz);
      int type = tctdbstrtoindextype(vbuf);
#if defined(DEBUG)
                fprintf(stderr, "index.key=%s\n", kbuf);
                fprintf(stderr, "index.opt='%s':%d\n", vbuf, type);
#endif
      if (type == TDBITVOID) {
        err = !tcDBIndexRemove(aDB, kbuf, ksiz);
#if defined(DEBUG)
        if (err) {
          fprintf(stderr, "FieldName:%s; FieldOpt:%s Remove Index error! \n", kbuf, vbuf);
        }
#endif
      }else if (type == TDBITREINDEX) {
        err = !tcDBReIndex(aDB, kbuf, ksiz, vbuf, vsiz, type);
      } else if(type >= 0){
        err = !tcDBIndexSet(aDB, kbuf, ksiz, vbuf, vsiz, type);
#if defined(DEBUG)
        if (err) {
          fprintf(stderr, "FieldName:%s; FieldOpt:%s Set Index error! \n", kbuf, vbuf);
        }
#endif
      } else {
        err = true;
      }
    }
    if(err){
      tclistdel(rv);
      rv = NULL;
    }
  } else if(!strcmp(name, "search") || !strcmp(name, "metasearch")){
#if defined(DEBUG)
      fprintf(stderr, "searching... \n");
    //return NULL;
#endif
    bool toout = false;
    bool tocnt = false;
    bool tohint = false;
    TDBQuery *qry = tcDBQueryCreate(aDB);
    TDBQuery **qrys = NULL;
    int qnum = 0;
    int mstype = TDBMSUNION;
    //cnames: column names
    TCLIST *cnames = NULL;
    for(int i = 0; i < argc; i++){
      const char *arg;
      int asiz;
      TCLISTVAL(arg, args, i, asiz);
      TCLIST *tokens = tcstrsplit2(arg, asiz);
      int tnum = TCLISTNUM(tokens);
      if(tnum > 0){
        const char *cmd = TCLISTVALPTR(tokens, 0);
        if((!strcmp(cmd, "addcond") || !strcmp(cmd, "cond")) && tnum > 3){
          const char *name = TCLISTVALPTR(tokens, 1);
          const char *opstr = TCLISTVALPTR(tokens, 2);
          const char *expr = TCLISTVALPTR(tokens, 3);
          int op = tctdbqrystrtocondop(opstr);
          if(op >= 0) tcDBQueryAddCondition(qry, name, op, expr);
        } else if((!strcmp(cmd, "setorder") || !strcmp(cmd, "order")) && tnum > 2){
          const char *name = TCLISTVALPTR(tokens, 1);
          const char *typestr = TCLISTVALPTR(tokens, 2);
          int type = tctdbqrystrtoordertype(typestr);
          if(type >= 0) tcDBQuerySetOrder(qry, name, type);
        } else if((!strcmp(cmd, "setlimit") || !strcmp(cmd, "limit") ||
                   !strcmp(cmd, "setmax") || !strcmp(cmd, "max") ) && tnum > 1){
          const char *maxstr = TCLISTVALPTR(tokens, 1);
          int max = tcatoi(maxstr);
          int skip = 0;
          if(tnum > 2){
            maxstr = TCLISTVALPTR(tokens, 2);
            skip = tcatoi(maxstr);
          }
          tcDBQuerySetLimit(qry, max, skip);
        } else if(!strcmp(cmd, "get") || !strcmp(cmd, "columns")){
          if(!cnames) cnames = tclistnew();
          for(int j = 1; j < tnum; j++){
            const char *token;
            int tsiz;
            TCLISTVAL(token, tokens, j, tsiz);
            TCLISTPUSH(cnames, token, tsiz);
          }
        } else if(!strcmp(cmd, "next")){
          if(qrys){
            TCREALLOC(qrys, qrys, sizeof(*qrys) * (qnum + 1));
          } else {
            TCMALLOC(qrys, sizeof(*qrys) * 2);
            qrys[0] = qry;
            qnum = 1;
          }
          qry = tcDBQueryCreate(aDB);
          qrys[qnum++] = qry;
        } else if(!strcmp(cmd, "mstype") && tnum > 1){
          const char *typestr = TCLISTVALPTR(tokens, 1);
          mstype = tctdbstrtometasearcytype(typestr);
          if(mstype < 0) mstype = TDBMSUNION;
        } else if(!strcmp(cmd, "out") || !strcmp(cmd, "remove")){
          toout = true;
        } else if(!strcmp(cmd, "count")){
          tocnt = true;
        } else if(!strcmp(cmd, "hint")){
          tohint = true;
        }
      }
      tclistdel(tokens);
    } //for
    if(toout){
      //return and remove matched records.
      if(cnames){
        rv = tclistnew2(1);
        void *opq[2];
        opq[0] = rv;
        opq[1] = cnames;
        if(!tcDBQueryProc2(qry, tcDBQueryGetOutProc, opq)){
          tclistdel(rv);
          rv = NULL;
        }
      } else {
        if(tcDBQuerySearchDelete(qry)){
          rv = tclistnew2(1);
        } else {
          rv = NULL;
        }
      }
    } else {
      if(qrys){
        rv = tcDBMetaSearch(qrys, qnum, mstype);
      } else {
        rv = tcDBQuerySearch(qry);
      }
      if(cnames){
        int vColumnCount = TCLISTNUM(cnames);
        int rnum = TCLISTNUM(rv);
        TCLIST *nrv = tclistnew2(rnum);
        for(int i = 0; i < rnum; i++){
          const char *pkbuf;
          int pksiz;
          TCLISTVAL(pkbuf, rv, i, pksiz);// get the matched key
          //get the column value for the column name. [TODO]
          TCMAP *cols = tcDBGetColumnValuesMapImpl(vDB, pkbuf, pksiz);
          if(cols){
            tcmapput(cols, "", 0, pkbuf, pksiz);
            tcmapmove(cols, "", 0, true);
            if(vColumnCount > 0){
              TCMAP *ncols = tcmapnew2(vColumnCount + 1);
              for(int j = 0; j < vColumnCount; j++){
                const char *cname;
                int cnsiz;
                TCLISTVAL(cname, cnames, j, cnsiz);
                int cvsiz;
                const char *cvalue = tcmapget(cols, cname, cnsiz, &cvsiz);
                if(cvalue) tcmapput(ncols, cname, cnsiz, cvalue, cvsiz);
              }
              tcmapdel(cols);
              cols = ncols;
            }
            int csiz;
            char *cbuf = tcstrjoin4(cols, &csiz);
            tclistpushmalloc(nrv, cbuf, csiz);
            tcmapdel(cols);
          }
        }
        tclistdel(rv);
        rv = nrv;
      }
    }
    if(tocnt && rv){
      tclistclear(rv);
      char numbuf[TCNUMBUFSIZ];
      int len = sprintf(numbuf, "%d", tcDBQueryCount(qry));
      TCLISTPUSH(rv, numbuf, len);
    }
    if(tohint && rv){
      TCXSTR *hbuf = tcxstrnew();
      TCXSTRCAT(hbuf, "", 1);
      TCXSTRCAT(hbuf, "", 1);
      TCXSTRCAT(hbuf, "[[HINT]]\n", 9);
      const char *hint = tcDBQueryHint(qrys ? qrys[0] : qry);
      TCXSTRCAT(hbuf, hint, strlen(hint));
      TCLISTPUSH(rv, TCXSTRPTR(hbuf), TCXSTRSIZE(hbuf));
      tcxstrdel(hbuf);
    }
    if(cnames) tclistdel(cnames);
    if(qrys){
      for(int i = 0; i < qnum; i++){
        tcDBQueryFree(qrys[i]);
      }
      TCFREE(qrys);
    } else {
      tcDBQueryFree(qry);
    }
  } else if(!strcmp(name, "sync")){
    rv = tclistnew2(1);
    if(!tcDBSync(aDB)){
      tclistdel(rv);
      rv = NULL;
    }
  } else if(!strcmp(name, "optimize")){
    rv = tclistnew2(1);
    const char *params = argc > 0 ? TCLISTVALPTR(args, 0) : NULL;
    if(!tcDBOptimize(aDB, params)){
      tclistdel(rv);
      rv = NULL;
    }
  } else if(!strcmp(name, "vanish")){
    rv = tclistnew2(1);
    if(!tcDBClear(aDB)){
      tclistdel(rv);
      rv = NULL;
    }
  } else if(!strcmp(name, "error")){
    rv = tclistnew2(1);
    int ecode = tcbdbecode(vDB->DB);
    tclistprintf(rv, "%d: %s", ecode, tcbdberrmsg(ecode));
    uint8_t flags = tcbdbflags(vDB->DB);
    if(flags & BDBFFATAL) tclistprintf(rv, "fatal");
  } else if(!strcmp(name, "defrag")){
    rv = tclistnew2(1);
    int64_t step = argc > 0 ? tcatoi(TCLISTVALPTR(args, 0)) : -1;
    if(!tcDBDefrag(vDB, step)){
      tclistdel(rv);
      rv = NULL;
    }
  } else if(!strcmp(name, "cacheclear")){
    rv = tclistnew2(1);
    if(!tcDBCacheClear(vDB)){
      tclistdel(rv);
      rv = NULL;
    }
  } else if(!strcmp(name, "regex")){
    if(argc > 0){
      const char *regex = TCLISTVALPTR(args, 0);
      int options = REG_EXTENDED | REG_NOSUB;
      if(*regex == '*'){
        options |= REG_ICASE;
        regex++;
      }
      regex_t rbuf;
      if(regcomp(&rbuf, regex, options) == 0){
        rv = tclistnew();
        int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
        if(max < 1) max = INT_MAX;
        BDBCUR *cur = tcbdbcurnew(vDB->DB);
        tcbdbcurfirst(cur);
        TCXSTR *kxstr = tcxstrnew();
        TCXSTR *vxstr = tcxstrnew();
        while(max > 0 && tcbdbcurrec(cur, kxstr, vxstr)){
          const char *kbuf = TCXSTRPTR(kxstr);
          if(regexec(&rbuf, kbuf, 0, NULL, 0) == 0){
            TCLISTPUSH(rv, kbuf, TCXSTRSIZE(kxstr));
            TCLISTPUSH(rv, TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr));
            max--;
          }
          tcbdbcurnext(cur);
        }
        tcxstrdel(vxstr);
        tcxstrdel(kxstr);
        tcbdbcurdel(cur);
        regfree(&rbuf);
      } else {
        rv = NULL;
      }
    } else {
      rv = NULL;
    }
  } else if(!strcmp(name, "range")){
    rv = tclistnew();
    int bksiz = 0;
    const char *bkbuf = NULL;
    if(argc > 0) TCLISTVAL(bkbuf, args, 0, bksiz);
    int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
    if(max < 1) max = INT_MAX;
    int eksiz = 0;
    const char *ekbuf = NULL;
    if(argc > 2) TCLISTVAL(ekbuf, args, 2, eksiz);
    TCCMP cmp = tcbdbcmpfunc(vDB->DB);
    void *cmpop = tcbdbcmpop(vDB->DB);
    BDBCUR *cur = tcbdbcurnew(vDB->DB);
    if(bkbuf){
      tcbdbcurjump(cur, bkbuf, bksiz);
    } else {
      tcbdbcurfirst(cur);
    }
    TCXSTR *kxstr = tcxstrnew();
    TCXSTR *vxstr = tcxstrnew();
    while(max > 0 && tcbdbcurrec(cur, kxstr, vxstr)){
      const char *kbuf = TCXSTRPTR(kxstr);
      int ksiz = TCXSTRSIZE(kxstr);
      if(ekbuf && cmp(kbuf, ksiz, ekbuf, eksiz, cmpop) >= 0) break;
      TCLISTPUSH(rv, kbuf, ksiz);
      TCLISTPUSH(rv, TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr));
      max--;
      tcbdbcurnext(cur);
    }
    tcxstrdel(vxstr);
    tcxstrdel(kxstr);
    tcbdbcurdel(cur);
  } else if(!strcmp(name, "genuid")){
    rv = tclistnew2(1);
    char numbuf[TCNUMBUFSIZ];
    int nsiz = sprintf(numbuf, "%lld", (long long)tcDBGenUID(aDB));
    TCLISTPUSH(rv, numbuf, nsiz);
  } else if(!strcmp(name, "genuuid")){
    rv = tclistnew2(1);
     char *vUUID = GenerateUID();
     tclistpush2(rv, vUUID);
     TCFREE(vUUID);
  } else if(!strcmp(name, "getindex")){
    if(argc > 0){
      rv = tclistnew2(1);
      const char *kbuf;
      int ksiz;
      TCLISTVAL(kbuf, args, 0, ksiz);
      TCLIST *vals = tcbdbget4(vDB->IndexDB, kbuf, ksiz);
      if(vals){
        tclistdel(rv);
        rv = vals;
#if defined(DEBUG)
        fprintf(stderr, "getindex(%s) argc=%d\n", name, argc);
        for(int i = 0; i < TCLISTNUM(rv); i++){
          const char *vbuf;
          int vsiz;
          TCLISTVAL(vbuf, rv, i, vsiz);
          fprintf(stderr, "  value[%d]: %s\n", i, vbuf);
        }
#endif
      } else {
        tclistdel(rv);
        rv = NULL;
      }
    } else {
      rv = NULL;
    }
  } else {
    rv = NULL;
  }
  return rv;  
}


// END OF FILE
